쿠버네티스 문서에서 이 섹션은 개별의 태스크를 수행하는 방법을 보여준다. 한 태스크 페이지는 일반적으로 여러 단계로 이루어진 짧은 시퀀스를 제공함으로써, 하나의 일을 수행하는 방법을 보여준다.
만약 태스크 페이지를 작성하고 싶다면, 문서 풀 리퀘스트(Pull Request) 생성하기를 참조한다.
이 섹션의 다중 페이지 출력 화면임. 여기를 클릭하여 프린트.
쿠버네티스 문서에서 이 섹션은 개별의 태스크를 수행하는 방법을 보여준다. 한 태스크 페이지는 일반적으로 여러 단계로 이루어진 짧은 시퀀스를 제공함으로써, 하나의 일을 수행하는 방법을 보여준다.
만약 태스크 페이지를 작성하고 싶다면, 문서 풀 리퀘스트(Pull Request) 생성하기를 참조한다.
쿠버네티스 커맨드 라인 도구인 kubectl
을 사용하면
쿠버네티스 클러스터에 대해 명령을 실행할 수 있다.
kubectl
을 사용하여 애플리케이션을 배포하고, 클러스터 리소스를 검사 및 관리하고,
로그를 볼 수 있다. kubectl 전체 명령어를 포함한 추가 정보는
kubectl
레퍼런스 문서에서 확인할 수 있다.
kubectl
은 다양한 리눅스 플랫폼, macOS, 그리고 윈도우에 설치할 수 있다.
각각에 대한 설치 가이드는 다음과 같다.
kind를 사용하면 로컬 컴퓨터에서 쿠버네티스를 실행할 수 있다. 이 도구를 사용하려면 도커를 설치하고 구성해야 한다.
kind 퀵 스타트 페이지는 kind를 시작하고 실행하기 위해 수행해야 하는 작업을 보여준다.
kind
와 마찬가지로, minikube
는 쿠버네티스를 로컬에서 실행할 수 있는
도구이다. minikube
는 개인용 컴퓨터(윈도우, macOS 및 리눅스 PC 포함)에서 올인원 방식 또는
복수 개의 노드로 쿠버네티스 클러스터를 실행하여, 쿠버네티스를 사용해보거나 일상적인 개발 작업을
수행할 수 있다.
도구 설치에 중점을 두고 있다면 공식 사이트에서의 시작하기! 가이드를 따라 해볼 수 있다.
minikube
가 작동하면, 이를 사용하여
샘플 애플리케이션을 실행해볼 수 있다.
kubeadm 도구를 사용하여 쿠버네티스 클러스터를 만들고 관리할 수 있다. 사용자 친화적인 방식으로 최소한의 실행 가능하고 안전한 클러스터를 설정하고 실행하는 데 필요한 작업을 수행한다.
kubeadm 설치 페이지는 kubeadm 설치하는 방법을 보여준다. 설치가 끝나면, 클러스터 생성이 가능하다.
클러스터의 마이너(minor) 버전 차이 내에 있는 kubectl 버전을 사용해야 한다. 예를 들어, v1.31 클라이언트는 v1.30, v1.31, v1.32의 컨트롤 플레인과 연동될 수 있다. 호환되는 최신 버전의 kubectl을 사용하면 예기치 않은 문제를 피할 수 있다.
다음과 같은 방법으로 macOS에 kubectl을 설치할 수 있다.
최신 릴리스를 다운로드한다.
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl"
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl"
특정 버전을 다운로드하려면, $(curl -L -s https://dl.k8s.io/release/stable.txt)
명령 부분을 특정 버전으로 바꾼다.
예를 들어, Intel macOS에 버전 1.31.0을 다운로드하려면, 다음을 입력한다.
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/darwin/amd64/kubectl"
Apple Silicon의 macOS라면, 다음을 입력한다.
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/darwin/arm64/kubectl"
바이너리를 검증한다. (선택 사항)
kubectl 체크섬 파일을 다운로드한다.
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl.sha256"
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl.sha256"
kubectl 바이너리를 체크섬 파일을 통해 검증한다.
echo "$(cat kubectl.sha256) kubectl" | shasum -a 256 --check
검증이 성공한다면, 출력은 다음과 같다.
kubectl: OK
검증이 실패한다면, shasum
이 0이 아닌 상태로 종료되며 다음과 유사한 결과를 출력한다.
kubectl: FAILED
shasum: WARNING: 1 computed checksum did NOT match
kubectl 바이너리를 실행 가능하게 한다.
chmod +x ./kubectl
kubectl 바이너리를 시스템 PATH
의 파일 위치로 옮긴다.
sudo mv ./kubectl /usr/local/bin/kubectl
sudo chown root: /usr/local/bin/kubectl
PATH
환경 변수 안에 /usr/local/bin
이 있는지 확인한다.설치한 버전이 최신 버전인지 확인한다.
kubectl version --client
또는 다음을 실행하여 버전에 대한 더 자세한 정보를 본다.
kubectl version --client --output=yaml
macOS에서 Homebrew 패키지 관리자를 사용하는 경우, Homebrew로 kubectl을 설치할 수 있다.
설치 명령을 실행한다.
brew install kubectl
또는
brew install kubernetes-cli
설치한 버전이 최신 버전인지 확인한다.
kubectl version --client
macOS에서 Macports 패키지 관리자를 사용하는 경우, Macports로 kubectl을 설치할 수 있다.
설치 명령을 실행한다.
sudo port selfupdate
sudo port install kubectl
설치한 버전이 최신 버전인지 확인한다.
kubectl version --client
kubectl이 쿠버네티스 클러스터를 찾아 접근하려면,
kube-up.sh를
사용하여 클러스터를 생성하거나 Minikube 클러스터를 성공적으로 배포할 때 자동으로 생성되는
kubeconfig 파일이
필요하다.
기본적으로, kubectl 구성은 ~/.kube/config
에 있다.
클러스터 상태를 가져와서 kubectl이 올바르게 구성되어 있는지 확인한다.
kubectl cluster-info
URL 응답이 표시되면, kubectl이 클러스터에 접근하도록 올바르게 구성된 것이다.
다음과 비슷한 메시지가 표시되면, kubectl이 올바르게 구성되지 않았거나 쿠버네티스 클러스터에 연결할 수 없다.
The connection to the server <server-name:port> was refused - did you specify the right host or port?
예를 들어, 랩톱에서 로컬로 쿠버네티스 클러스터를 실행하려면, Minikube와 같은 도구를 먼저 설치한 다음 위에서 언급한 명령을 다시 실행해야 한다.
kubectl cluster-info
가 URL 응답을 반환하지만 클러스터에 접근할 수 없는 경우, 올바르게 구성되었는지 확인하려면 다음을 사용한다.
kubectl cluster-info dump
kubectl은 Bash, Zsh, Fish, 및 PowerShell에 대한 자동 완성 지원을 제공하므로 입력을 위한 타이핑을 많이 절약할 수 있다.
다음은 Bash, Fish, 및 Zsh에 대한 자동 완성을 설정하는 절차이다.
Bash의 kubectl 자동 완성 스크립트는 kubectl completion bash
로 생성할 수 있다. 이 스크립트를 셸에 소싱하면 kubectl 자동 완성이 가능하다.
그러나 kubectl 자동 완성 스크립트는 미리 bash-completion을 설치해야 동작한다.
여기의 지침에서는 Bash 4.1 이상을 사용한다고 가정한다. 다음을 실행하여 Bash 버전을 확인할 수 있다.
echo $BASH_VERSION
너무 오래된 버전인 경우, Homebrew를 사용하여 설치/업그레이드할 수 있다.
brew install bash
셸을 다시 로드하고 원하는 버전을 사용 중인지 확인한다.
echo $BASH_VERSION $SHELL
Homebrew는 보통 /usr/local/bin/bash
에 설치한다.
bash-completion v2가 이미 설치되어 있는지 type_init_completion
으로 확인할 수 있다. 그렇지 않은 경우, Homebrew로 설치할 수 있다.
brew install bash-completion@2
이 명령의 출력에 명시된 바와 같이, ~/.bash_profile
파일에 다음을 추가한다.
export BASH_COMPLETION_COMPAT_DIR="/usr/local/etc/bash_completion.d"
[[ -r "/usr/local/etc/profile.d/bash_completion.sh" ]] && . "/usr/local/etc/profile.d/bash_completion.sh"
셸을 다시 로드하고 bash-completion v2가 올바르게 설치되었는지 type _init_completion
으로 확인한다.
이제 kubectl 자동 완성 스크립트가 모든 셸 세션에서 제공되도록 해야 한다. 이를 수행하는 방법에는 여러 가지가 있다.
자동 완성 스크립트를 ~/.bash_profile
파일에서 소싱한다.
echo 'source <(kubectl completion bash)' >>~/.bash_profile
자동 완성 스크립트를 /usr/local/etc/bash_completion.d
디렉터리에 추가한다.
kubectl completion bash >/usr/local/etc/bash_completion.d/kubectl
kubectl에 대한 앨리어스가 있는 경우, 해당 앨리어스로 작업하기 위해 셸 자동 완성을 확장할 수 있다.
echo 'alias k=kubectl' >>~/.bash_profile
echo 'complete -o default -F __start_kubectl k' >>~/.bash_profile
Homebrew로 kubectl을 설치한 경우(여기의 설명을 참고), kubectl 자동 완성 스크립트가 이미 /usr/local/etc/bash_completion.d/kubectl
에 있을 것이다. 이 경우, 아무 것도 할 필요가 없다.
BASH_COMPLETION_COMPAT_DIR
디렉터리의 모든 파일을 소싱하므로, 후자의 두 가지 방법이 적용된다.어떤 경우든, 셸을 다시 로드하면, kubectl 자동 완성 기능이 작동할 것이다.
Fish용 kubectl 자동 완성 스크립트는 kubectl completion fish
명령으로 생성할 수 있다. 셸에서 자동 완성 스크립트를 소싱하면 kubectl 자동 완성 기능이 활성화된다.
모든 셸 세션에서 사용하려면, ~/.config/fish/config.fish
파일에 다음을 추가한다.
kubectl completion fish | source
셸을 다시 로드하면, kubectl 자동 완성 기능이 작동할 것이다.
Zsh용 kubectl 자동 완성 스크립트는 kubectl completion zsh
명령으로 생성할 수 있다. 셸에서 자동 완성 스크립트를 소싱하면 kubectl 자동 완성 기능이 활성화된다.
모든 셸 세션에서 사용하려면, ~/.zshrc
파일에 다음을 추가한다.
source <(kubectl completion zsh)
kubectl에 대한 앨리어스가 있는 경우, kubectl 자동완성이 자동으로 동작할 것이다.
셸을 다시 로드하면, kubectl 자동 완성 기능이 작동할 것이다.
2: command not found: compdef
와 같은 오류가 발생하면, ~/.zshrc
파일의 시작 부분에 다음을 추가한다.
autoload -Uz compinit
compinit
kubectl convert
플러그인 설치이것은 쿠버네티스 커맨드 라인 도구인 kubectl
의 플러그인으로서, 특정 버전의 쿠버네티스 API로 작성된 매니페스트를 다른 버전으로
변환할 수 있도록 한다. 이것은 매니페스트를 최신 쿠버네티스 릴리스의 사용 중단되지 않은 API로 마이그레이션하는 데 특히 유용하다.
더 많은 정보는 다음의 사용 중단되지 않은 API로 마이그레이션을 참고한다.
다음 명령으로 최신 릴리스를 다운로드한다.
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl-convert"
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl-convert"
바이너리를 검증한다. (선택 사항)
kubectl-convert 체크섬(checksum) 파일을 다운로드한다.
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl-convert.sha256"
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl-convert.sha256"
kubectl-convert 바이너리를 체크섬 파일을 통해 검증한다.
echo "$(cat kubectl-convert.sha256) kubectl-convert" | shasum -a 256 --check
검증이 성공한다면, 출력은 다음과 같다.
kubectl-convert: OK
검증이 실패한다면, shasum
이 0이 아닌 상태로 종료되며 다음과 유사한 결과를 출력한다.
kubectl-convert: FAILED
shasum: WARNING: 1 computed checksum did NOT match
kubectl-convert 바이너리를 실행 가능하게 한다.
chmod +x ./kubectl-convert
kubectl-convert 바이너리를 시스템 PATH
의 파일 위치로 옮긴다.
sudo mv ./kubectl-convert /usr/local/bin/kubectl-convert
sudo chown root: /usr/local/bin/kubectl-convert
PATH
환경 변수 안에 /usr/local/bin
이 있는지 확인한다.플러그인이 정상적으로 설치되었는지 확인한다.
kubectl convert --help
에러가 출력되지 않는다면, 플러그인이 정상적으로 설치된 것이다.
클러스터의 마이너(minor) 버전 차이 내에 있는 kubectl 버전을 사용해야 한다. 예를 들어, v1.31 클라이언트는 v1.30, v1.31, v1.32의 컨트롤 플레인과 연동될 수 있다. 호환되는 최신 버전의 kubectl을 사용하면 예기치 않은 문제를 피할 수 있다.
다음과 같은 방법으로 리눅스에 kubectl을 설치할 수 있다.
다음 명령으로 최신 릴리스를 다운로드한다.
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
특정 버전을 다운로드하려면, $(curl -L -s https://dl.k8s.io/release/stable.txt)
명령 부분을 특정 버전으로 바꾼다.
예를 들어, 리눅스에서 버전 1.31.0을 다운로드하려면, 다음을 입력한다.
curl -LO https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubectl
바이너리를 검증한다. (선택 사항)
kubectl 체크섬(checksum) 파일을 다운로드한다.
curl -LO "https://dl.k8s.io/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl.sha256"
kubectl 바이너리를 체크섬 파일을 통해 검증한다.
echo "$(cat kubectl.sha256) kubectl" | sha256sum --check
검증이 성공한다면, 출력은 다음과 같다.
kubectl: OK
검증이 실패한다면, shasum
이 0이 아닌 상태로 종료되며 다음과 유사한 결과를 출력한다.
kubectl: FAILED
sha256sum: WARNING: 1 computed checksum did NOT match
kubectl 설치
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
대상 시스템에 root 접근 권한을 가지고 있지 않더라도, ~/.local/bin
디렉터리에 kubectl을 설치할 수 있다.
chmod +x kubectl
mkdir -p ~/.local/bin
mv ./kubectl ~/.local/bin/kubectl
# 그리고 ~/.local/bin 을 $PATH의 앞부분 또는 뒷부분에 추가
설치한 버전이 최신인지 확인한다.
kubectl version --client
또는 다음을 실행하여 버전에 대한 더 자세한 정보를 본다.
kubectl version --client --output=yaml
apt
패키지 색인을 업데이트하고 쿠버네티스 apt
리포지터리를 사용하는 데 필요한 패키지들을 설치한다.
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
Debian 9(stretch) 또는 그 이전 버전을 사용하는 경우 apt-transport-https
도 설치해야 한다.
sudo apt-get install -y apt-transport-https
구글 클라우드 공개 사이닝 키를 다운로드한다.
sudo curl -fsSLo /etc/apt/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
쿠버네티스 apt
리포지터리를 추가한다.
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
새 리포지터리의 apt
패키지 색인을 업데이트하고 kubectl을 설치한다.
sudo apt-get update
sudo apt-get install -y kubectl
/etc/apt/keyrings
파일이 존재하지 않는다.
필요할 경우, 읽기 권한은 모두에게 부여되지만 쓰기 권한은 관리자만 갖도록 해당 디렉토리를 생성한다.cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
sudo yum install -y kubectl
kubectl이 쿠버네티스 클러스터를 찾아 접근하려면,
kube-up.sh를
사용하여 클러스터를 생성하거나 Minikube 클러스터를 성공적으로 배포할 때 자동으로 생성되는
kubeconfig 파일이
필요하다.
기본적으로, kubectl 구성은 ~/.kube/config
에 있다.
클러스터 상태를 가져와서 kubectl이 올바르게 구성되어 있는지 확인한다.
kubectl cluster-info
URL 응답이 표시되면, kubectl이 클러스터에 접근하도록 올바르게 구성된 것이다.
다음과 비슷한 메시지가 표시되면, kubectl이 올바르게 구성되지 않았거나 쿠버네티스 클러스터에 연결할 수 없다.
The connection to the server <server-name:port> was refused - did you specify the right host or port?
예를 들어, 랩톱에서 로컬로 쿠버네티스 클러스터를 실행하려면, Minikube와 같은 도구를 먼저 설치한 다음 위에서 언급한 명령을 다시 실행해야 한다.
kubectl cluster-info
가 URL 응답을 반환하지만 클러스터에 접근할 수 없는 경우, 올바르게 구성되었는지 확인하려면 다음을 사용한다.
kubectl cluster-info dump
kubectl은 Bash, Zsh, Fish, 및 PowerShell에 대한 자동 완성 지원을 제공하므로 입력을 위한 타이핑을 많이 절약할 수 있다.
다음은 Bash, Fish, 및 Zsh에 대한 자동 완성을 설정하는 절차이다.
Bash의 kubectl 자동 완성 스크립트는 kubectl completion bash
명령으로 생성할 수 있다. 셸에서 자동 완성 스크립트를 소싱(sourcing)하면 kubectl 자동 완성 기능이 활성화된다.
그러나, 자동 완성 스크립트는 bash-completion에 의존하고 있으며, 이 소프트웨어를 먼저 설치해야 한다(type _init_completion
을 실행하여 bash-completion이 이미 설치되어 있는지 확인할 수 있음).
bash-completion은 많은 패키지 관리자에 의해 제공된다(여기 참고). apt-get install bash-completion
또는 yum install bash-completion
등으로 설치할 수 있다.
위의 명령은 bash-completion의 기본 스크립트인 /usr/share/bash-completion/bash_completion
을 생성한다. 패키지 관리자에 따라, ~/.bashrc
파일에서 이 파일을 수동으로 소스(source)해야 한다.
확인하려면, 셸을 다시 로드하고 type _init_completion
을 실행한다. 명령이 성공하면, 이미 설정된 상태이고, 그렇지 않으면 ~/.bashrc
파일에 다음을 추가한다.
source /usr/share/bash-completion/bash_completion
셸을 다시 로드하고 type _init_completion
을 입력하여 bash-completion이 올바르게 설치되었는지 확인한다.
이제 kubectl 자동 완성 스크립트가 모든 셸 세션에서 제공되도록 해야 한다. 이를 수행할 수 있는 두 가지 방법이 있다.
echo 'source <(kubectl completion bash)' >>~/.bashrc
kubectl completion bash | sudo tee /etc/bash_completion.d/kubectl > /dev/null
kubectl에 대한 앨리어스(alias)가 있는 경우, 해당 앨리어스로 작업하도록 셸 자동 완성을 확장할 수 있다.
echo 'alias k=kubectl' >>~/.bashrc
echo 'complete -o default -F __start_kubectl k' >>~/.bashrc
/etc/bash_completion.d
에 있는 모든 자동 완성 스크립트를 소싱한다.두 방법 모두 동일하다. 셸을 다시 로드하면, kubectl 자동 완성 기능이 작동할 것이다.
셸의 현재 세션에서 bash 자동 완성을 활성화하려면 exec bash
를 실행한다.
exec bash
Fish용 kubectl 자동 완성 스크립트는 kubectl completion fish
명령으로 생성할 수 있다. 셸에서 자동 완성 스크립트를 소싱하면 kubectl 자동 완성 기능이 활성화된다.
모든 셸 세션에서 사용하려면, ~/.config/fish/config.fish
파일에 다음을 추가한다.
kubectl completion fish | source
셸을 다시 로드하면, kubectl 자동 완성 기능이 작동할 것이다.
Zsh용 kubectl 자동 완성 스크립트는 kubectl completion zsh
명령으로 생성할 수 있다. 셸에서 자동 완성 스크립트를 소싱하면 kubectl 자동 완성 기능이 활성화된다.
모든 셸 세션에서 사용하려면, ~/.zshrc
파일에 다음을 추가한다.
source <(kubectl completion zsh)
kubectl에 대한 앨리어스가 있는 경우, kubectl 자동완성이 자동으로 동작할 것이다.
셸을 다시 로드하면, kubectl 자동 완성 기능이 작동할 것이다.
2: command not found: compdef
와 같은 오류가 발생하면, ~/.zshrc
파일의 시작 부분에 다음을 추가한다.
autoload -Uz compinit
compinit
kubectl convert
플러그인 설치이것은 쿠버네티스 커맨드 라인 도구인 kubectl
의 플러그인으로서, 특정 버전의 쿠버네티스 API로 작성된 매니페스트를 다른 버전으로
변환할 수 있도록 한다. 이것은 매니페스트를 최신 쿠버네티스 릴리스의 사용 중단되지 않은 API로 마이그레이션하는 데 특히 유용하다.
더 많은 정보는 다음의 사용 중단되지 않은 API로 마이그레이션을 참고한다.
다음 명령으로 최신 릴리스를 다운로드한다.
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl-convert"
바이너리를 검증한다. (선택 사항)
kubectl-convert 체크섬(checksum) 파일을 다운로드한다.
curl -LO "https://dl.k8s.io/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl-convert.sha256"
kubectl-convert 바이너리를 체크섬 파일을 통해 검증한다.
echo "$(cat kubectl-convert.sha256) kubectl-convert" | sha256sum --check
검증이 성공한다면, 출력은 다음과 같다.
kubectl-convert: OK
검증이 실패한다면, sha256
이 0이 아닌 상태로 종료되며 다음과 유사한 결과를 출력한다.
kubectl-convert: FAILED
sha256sum: WARNING: 1 computed checksum did NOT match
kubectl-convert 설치
sudo install -o root -g root -m 0755 kubectl-convert /usr/local/bin/kubectl-convert
플러그인이 정상적으로 설치되었는지 확인한다.
kubectl convert --help
에러가 출력되지 않는다면, 플러그인이 정상적으로 설치된 것이다.
클러스터의 마이너(minor) 버전 차이 내에 있는 kubectl 버전을 사용해야 한다. 예를 들어, v1.31 클라이언트는 v1.30, v1.31, v1.32의 컨트롤 플레인과 연동될 수 있다. 호환되는 최신 버전의 kubectl을 사용하면 예기치 않은 문제를 피할 수 있다.
다음과 같은 방법으로 윈도우에 kubectl을 설치할 수 있다.
최신 패치 릴리스 1.31 다운로드: kubectl 1.31.0
또는 curl
을 설치한 경우, 다음 명령을 사용한다.
curl.exe -LO "https://dl.k8s.io/release/v1.31.0/bin/windows/amd64/kubectl.exe"
바이너리를 검증한다. (선택 사항)
kubectl
체크섬 파일을 다운로드한다.
curl.exe -LO "https://dl.k8s.io/v1.31.0/bin/windows/amd64/kubectl.exe.sha256"
kubectl
바이너리를 체크섬 파일을 통해 검증한다.
커맨드 프롬프트를 사용하는 경우, CertUtil
의 출력과 다운로드한 체크섬 파일을 수동으로 비교한다.
CertUtil -hashfile kubectl.exe SHA256
type kubectl.exe.sha256
PowerShell을 사용하는 경우, -eq
연산자를 통해 True
또는 False
결과가 출력되는 자동 검증을 수행한다.
$($(CertUtil -hashfile .\kubectl.exe SHA256)[1] -replace " ", "") -eq $(type .\kubectl.exe.sha256)
kubectl
바이너리가 있는 폴더를 PATH
환경 변수의 앞부분 또는 뒷부분에 추가
kubectl
의 버전이 다운로드한 버전과 같은지 확인한다.
kubectl version --client
또는 다음을 실행하여 버전에 대한 더 자세한 정보를 본다.
kubectl version --client --output=yaml
kubectl
을 PATH
에 추가한다.
도커 데스크톱을 이전에 설치한 경우, 도커 데스크톱 설치 프로그램에서 추가한 PATH
항목 앞에 PATH
항목을 배치하거나 도커 데스크톱의 kubectl
을 제거해야 할 수도 있다.윈도우에 kubectl을 설치하기 위해서 Chocolatey 패키지 관리자, Scoop 커맨드 라인 설치 프로그램, winget 패키지 관리자를 사용할 수 있다.
choco install kubernetes-cli
scoop install kubectl
winget install -e --id Kubernetes.kubectl
설치한 버전이 최신 버전인지 확인한다.
kubectl version --client
홈 디렉터리로 이동한다.
# cmd.exe를 사용한다면, 다음을 실행한다. cd %USERPROFILE%
cd ~
.kube
디렉터리를 생성한다.
mkdir .kube
금방 생성한 .kube
디렉터리로 이동한다.
cd .kube
원격 쿠버네티스 클러스터를 사용하도록 kubectl을 구성한다.
New-Item config -type file
kubectl이 쿠버네티스 클러스터를 찾아 접근하려면,
kube-up.sh를
사용하여 클러스터를 생성하거나 Minikube 클러스터를 성공적으로 배포할 때 자동으로 생성되는
kubeconfig 파일이
필요하다.
기본적으로, kubectl 구성은 ~/.kube/config
에 있다.
클러스터 상태를 가져와서 kubectl이 올바르게 구성되어 있는지 확인한다.
kubectl cluster-info
URL 응답이 표시되면, kubectl이 클러스터에 접근하도록 올바르게 구성된 것이다.
다음과 비슷한 메시지가 표시되면, kubectl이 올바르게 구성되지 않았거나 쿠버네티스 클러스터에 연결할 수 없다.
The connection to the server <server-name:port> was refused - did you specify the right host or port?
예를 들어, 랩톱에서 로컬로 쿠버네티스 클러스터를 실행하려면, Minikube와 같은 도구를 먼저 설치한 다음 위에서 언급한 명령을 다시 실행해야 한다.
kubectl cluster-info
가 URL 응답을 반환하지만 클러스터에 접근할 수 없는 경우, 올바르게 구성되었는지 확인하려면 다음을 사용한다.
kubectl cluster-info dump
kubectl은 Bash, Zsh, Fish, 및 PowerShell에 대한 자동 완성 지원을 제공하므로 입력을 위한 타이핑을 많이 절약할 수 있다.
다음은 PowerShell에 대한 자동 완성을 설정하는 절차이다.
PowerShell용 kubectl 자동 완성 스크립트는 kubectl completion powershell
명령으로 생성할 수 있다.
모든 셸 세션에서 사용하려면, $PROFILE
파일에 다음을 추가한다.
kubectl completion powershell | Out-String | Invoke-Expression
이 명령은 PowerShell을 실행할 때마다 자동 완성 스크립트를 재생성한다. 아니면, 생성된 스크립트를 $PROFILE
파일에 직접 추가할 수도 있다.
생성된 스크립트를 $PROFILE
파일에 직접 추가하려면, PowerShell 프롬프트에서 다음 명령줄을 실행한다.
kubectl completion powershell >> $PROFILE
셸을 다시 불러오면, kubectl 자동 완성이 동작할 것이다.
kubectl convert
플러그인 설치이것은 쿠버네티스 커맨드 라인 도구인 kubectl
의 플러그인으로서, 특정 버전의 쿠버네티스 API로 작성된 매니페스트를 다른 버전으로
변환할 수 있도록 한다. 이것은 매니페스트를 최신 쿠버네티스 릴리스의 사용 중단되지 않은 API로 마이그레이션하는 데 특히 유용하다.
더 많은 정보는 다음의 사용 중단되지 않은 API로 마이그레이션을 참고한다.
다음 명령으로 최신 릴리스를 다운로드한다.
curl.exe -LO "https://dl.k8s.io/release/v1.31.0/bin/windows/amd64/kubectl-convert.exe"
바이너리를 검증한다. (선택 사항)
kubectl-convert
체크섬(checksum) 파일을 다운로드한다.
curl.exe -LO "https://dl.k8s.io/v1.31.0/bin/windows/amd64/kubectl-convert.exe.sha256"
kubectl-convert
바이너리를 체크섬 파일을 통해 검증한다.
커맨드 프롬프트를 사용하는 경우, CertUtil
의 출력과 다운로드한 체크섬 파일을 수동으로 비교한다.
CertUtil -hashfile kubectl-convert.exe SHA256
type kubectl-convert.exe.sha256
PowerShell을 사용하는 경우, -eq
연산자를 통해 True
또는 False
결과가 출력되는 자동 검증을 수행한다.
$($(CertUtil -hashfile .\kubectl-convert.exe SHA256)[1] -replace " ", "") -eq $(type .\kubectl-convert.exe.sha256)
kubectl-convert
바이너리가 있는 폴더를 PATH
환경 변수의 앞부분 또는 뒷부분에 추가
플러그인이 정상적으로 설치되었는지 확인한다.
kubectl convert --help
에러가 출력되지 않는다면, 플러그인이 정상적으로 설치된 것이다.
때때로 문제가 발생할 수 있다. 이 가이드는 이러한 상황을 해결하기 위해 작성되었다. 문제 해결에는 다음 두 가지를 참고해 볼 수 있다.
여러분이 현재 사용중인 릴리스에 대한 알려진 이슈들을 다음의 릴리스 페이지에서 확인해 볼 수도 있다.
여러분의 문제가 위에 소개된 어떠한 가이드로도 해결할 수 없다면, 쿠버네티스 커뮤니티로부터 도움을 받을 수 있는 다양한 방법들을 시도해 볼 수 있다.
이 사이트의 문서들은 다양한 질문들에 대한 답변을 제공할 수 있도록 구성되어 있다.
개념은 쿠버네티스의 아키텍처와 각 컴포넌트들이 어떻게 동작하는지에 대해 설명하고,
시작하기는 쿠버네티스를 시작하는 데 유용한 지침들을 제공한다.
태스크는 흔히 사용되는 작업들을 수행하는 방법에 대해 소개하고,
튜토리얼은 실무, 산업 특화 혹은 종단간 개발에 특화된 시나리오를 통해 차근차근 설명한다.
레퍼런스 섹션에서는
쿠버네티스 API와
kubectl
과 같은 커맨드 라인 인터페이스(CLI)에 대한
상세한 설명을 다룬다.
여러분들이 겪고 있는 문제와 동일한 문제에 대한 도움을 위해 커뮤니티의 다른 사람들이 이미 질문을 올렸을 수 있다. 쿠버네티스 팀은 쿠버네티스 태그가 등록된 글들을 모니터링하고 있다. 발생한 문제에 도움이 되는 기존 질문이 없다면, 해당 질문이 스택 오버플로우에 적합한지와 새로운 질문을 올리는 방법에 대한 가이드를 읽은 뒤에 새로운 질문을 올리자!
쿠버네티스 슬랙의 #kubernetes-users
채널을 통해 쿠버네티스 커뮤니티의 여러 사람들을 접할 수도 있다.
쿠버네티스 슬랙을 사용하기 위해서는 등록이 필요한데, 다음을 통해 채널 초대 요청을 할 수 있다.
(누구나 가입할 수 있다). 슬랙 채널은 여러분이 어떠한 질문을 할 수 있도록 언제나 열려있다.
가입하고 나면 여러분의 웹 브라우저나 슬랙 앱을 통해 쿠버네티스 슬랙
에 참여할 수 있다.
쿠버네티스 슬랙에 참여하게 된다면, 다양한 주제의 흥미와 관련된 여러 채널들에 대해
살펴본다. 가령, 쿠버네티스를 처음 접하는 사람이라면
#kubernetes-novice
채널에 가입할 수 있다. 혹은, 만약 당신이 개발자라면
#kubernetes-contributors
채널에 가입할 수 있다.
또한 각 국가 및 사용 언어별 채널들이 여럿 존재한다. 사용하는 언어로 도움을 받거나 정보를 얻기 위해서는 다음의 채널에 참가한다.
국가 | 채널 |
---|---|
China(중국) | #cn-users , #cn-events |
Finland(핀란드) | #fi-users |
France(프랑스) | #fr-users , #fr-events |
Germany(독일) | #de-users , #de-events |
India(인도) | #in-users , #in-events |
Italy(이탈리아) | #it-users , #it-events |
Japan(일본) | #jp-users , #jp-events |
Korea(한국) | #kr-users |
Netherlands(네덜란드) | #nl-users |
Norway(노르웨이) | #norw-users |
Poland(폴란드) | #pl-users |
Russia(러시아) | #ru-users |
Spain(스페인) | #es-users |
Sweden(스웨덴) | #se-users |
Turkey(터키) | #tr-users , #tr-events |
공식 쿠버네티스 포럼에 참여하는 것도 추천되는 방법이다. discuss.kubernetes.io.
만약 여러분이 버그처럼 보이는 것을 발견했거나, 기능 추가 요청을 하기 위해서는 GitHub 이슈 트래킹 시스템을 사용한다.
이슈를 작성하기 전에는, 여러분의 이슈가 기존 이슈에서 이미 다뤄졌는지 검색해 본다.
버그를 보고하는 경우에는, 해당 문제를 어떻게 재현할 수 있는지에 관련된 상세한 정보를 포함한다. 포함되어야 하는 정보들은 다음과 같다.
kubectl version
이 문서는 컨테이너화된 애플리케이션의 이슈를 해결하기 위한 자원을 담고 있다. 쿠버네티스 리소스(예: 파드, 서비스, 스테이트풀셋)의 일반적 이슈, 컨테이너 종료 메시지 이해에 대한 조언, 실행 중인 컨테이너를 디버그하는 방법 등을 다룬다.
이 가이드는 쿠버네티스에 배포되었지만 제대로 동작하지 않는 애플리케이션을 디버깅하는 방법을 소개한다. 이 가이드는 클러스터 디버깅에 대한 것은 아니다. 클러스터 디버깅에 대해서는 이 가이드를 참고한다.
트러블슈팅의 첫 단계는 문제를 파악하는 것이다. 무엇이 문제인가? 파드인가, 레플리케이션 컨트롤러인가, 서비스인가?
파드 디버깅의 첫 번째 단계는 파드를 살펴 보는 것이다. 다음의 명령어를 사용하여 파드의 현재 상태와 최근 이벤트를 점검한다.
kubectl describe pods ${POD_NAME}
파드 내부 컨테이너의 상태를 확인한다. 모두 Running
상태인가? 최근에 재시작 되었는가?
파드의 상태에 따라 디버깅을 계속한다.
파드가 Pending
상태로 멈춰 있는 경우는, 노드에 스케줄 될 수 없음을 의미한다.
일반적으로 이것은 어떤 유형의 리소스가 부족하거나 스케줄링을 방해하는 다른 요인 때문이다.
상단의 kubectl describe ...
명령의 결과를 확인하자.
파드를 스케줄 할 수 없는 사유에 대한 스케줄러의 메세지가 있을 것이다. 다음과 같은 사유가 있을 수 있다.
리소스가 부족한 경우: 사용자 클러스터의 CPU 나 메모리가 고갈되었을 수 있다. 이러한 경우, 파드를 삭제하거나, 리소스 요청을 조정하거나, 클러스터에 노드를 추가해야 한다. 컴퓨트 자원 문서에서 더 많은 정보를 확인한다.
hostPort
를 사용하고 있는 경우: 파드를 hostPort
에 바인딩할 때, 파드가 스케줄링될 수 있는 장소 수 제한이 존재한다.
대부분의 경우 hostPort
는 불필요하므로, 파드를 노출하기 위해서는 서비스(Service) 오브젝트 사용을 고려해 본다.
hostPort
가 꼭 필요하다면 클러스터의 노드 수 만큼만 파드를 스케줄링할 수 있다.
파드가 Waiting
상태에서 멈춘 경우는, 파드가 워커 노드에 스케줄링되었지만 해당 노드에서 실행될 수 없음을 의미한다.
다시 말하지만, kubectl describe ...
명령은 유용한 정보를 제공한다. 파드가 Waiting
상태에서 멈추는 가장 흔한 원인은 이미지 풀링(pulling)에 실패했기 때문이다. 다음의 3가지 사항을 확인한다.
docker pull <image>
명령을 실행한다.일단 사용자의 파드가 스케줄 되면, 구동중인 파드 디버그하기에 있는 방법을 사용하여 디버깅을 할 수 있다.
파드가 예상과 다르게 동작 중이라면, 파드 상세(예: 로컬 머신에 있는 mypod.yaml
파일)에 에러가 있었는데
파드 생성 시에 에러가 조용히 지나쳐진 경우일 수 있다.
종종 파드 상세의 들여쓰기가 잘못되었거나,
키 이름에 오타가 있어서 해당 키가 무시되는 일이 있을 수 있다.
예를 들어, command
를 commnd
로 잘못 기재했다면
해당 파드는 생성은 되지만 명시한 명령줄을 실행하지 않을 것이다.
가장 먼저 해야 할 일은 파드를 삭제한 다음, --validate
옵션을 사용하여 다시 만들어 보는 것이다.
예를 들어, kubectl apply --validate -f mypod.yaml
를 실행한다.
command
를 commnd
로 잘못 기재했다면 다음과 같은 에러가 발생할 것이다.
I0805 10:43:25.129850 46757 schema.go:126] unknown field: commnd
I0805 10:43:25.129973 46757 schema.go:129] this may be a false alarm, see https://github.com/kubernetes/kubernetes/issues/6842
pods/mypod
다음으로 확인할 것은 apiserver를 통해 확인한 파드 상세가
사용자가 의도한 파드 상세(예: 로컬 머신에 있는 yaml 파일)와 일치하는지 여부이다.
예를 들어, kubectl get pods/mypod -o yaml > mypod-on-apiserver.yaml
를 실행한 다음,
원본 파드 상세(mypod.yaml
)와 apiserver를 통해 확인한 파드 상세(mypod-on-apiserver.yaml
)를 수동으로 비교한다.
보통 원본 버전에는 없지만 "apiserver" 버전에는 있는 줄들이 존재한다.
이는 예상대로이다.
하지만, 원본 버전에는 있지만 "apiserver" 버전에는 없는 줄들이 있다면,
이는 원본 파드 상세에 문제가 있을 수도 있음을 의미한다.
레플리케이션컨트롤러의 경우에는 매우 직관적이다. 파드 생성이 가능하거나 또는 불가능한 경우 둘 뿐이다. 레플리케이션컨트롤러가 파드를 생성할 수 없다면, 위의 지침을 참고하여 파드를 디버깅한다.
사용자는 kubectl describe rc ${CONTROLLER_NAME}
을 사용하여
레플리케이션 컨트롤러와 관련된 이벤트를 검사할 수도 있다.
서비스는 파드 집합에 대한 로드 밸런싱 기능을 제공한다. 일반적인 몇몇 문제들 때문에 서비스가 제대로 동작하지 않을 수 있다. 다음 지침을 이용하여 서비스 문제를 디버깅할 수 있다.
먼저, 서비스를 위한 엔드포인트가 존재하는지 확인한다. 모든 서비스 오브젝트에 대해, apiserver는 endpoints
리소스를 생성하고 사용 가능한(available) 상태로 만든다.
다음 명령을 사용하여 이 리소스를 볼 수 있다.
kubectl get endpoints ${SERVICE_NAME}
엔드포인트의 수가 해당 서비스에 속하는 파드의 수와 일치하는지 확인한다. 예를 들어, 서비스가 레플리카 3개인 nginx 컨테이너를 위한 것이라면, 서비스의 엔드포인트 항목에서 서로 다른 3개의 IP 주소가 확인되어야 한다.
엔드포인트가 없는 상태라면, 서비스가 사용 중인 레이블을 이용하여 파드 목록을 조회해 본다. 다음과 같은 레이블을 갖는 서비스를 가정한다.
...
spec:
- selector:
name: nginx
type: frontend
다음의 명령을 사용하여,
kubectl get pods --selector=name=nginx,type=frontend
이 셀렉터에 매치되는 파드 목록을 조회할 수 있다. 서비스에 속할 것으로 예상하는 파드가 모두 조회 결과에 있는지 확인한다.
파드의 containerPort
가 서비스의 targetPort
와 일치하는지 확인한다.
서비스 디버깅하기에서 더 많은 정보를 확인한다.
위의 방법 중 어떤 것으로도 문제가 해결되지 않는다면,
서비스 디버깅하기 문서를 참조하여
서비스
가 실행 중인지, 서비스에 엔드포인트
가 있는지, 파드
가 실제로 서빙 중인지 확인한다.
예를 들어, DNS가 실행 중이고, iptables 규칙이 설정되어 있고, kube-proxy가 정상적으로 동작하는 것으로 보이는 상황이라면,
위와 같은 사항을 확인해 볼 수 있다.
트러블슈팅 문서에서 더 많은 정보를 볼 수도 있다.
쿠버네티스를 새로 설치할 때 자주 발생하는 문제 중 하나는 서비스가 제대로 작동하지 않는 현상이다. 디플로이먼트(또는 다른 워크로드 컨트롤러)를 통해 파드를 실행하고 서비스를 생성했지만, 해당 서비스에 엑세스하려고 하면 응답이 없는 경우이다. 이 문서를 통해 무엇이 잘못되었는지 파악하는 데 도움이 되기를 바란다.
이 페이지의 많은 단계에서, 클러스터 내에 실행 중인 파드가 어떤 것을 보고 있는지 알고 싶을 것이다. 이를 수행하는 가장 간단한 방법은 대화형 busybox 파드를 실행하는 것이다:
kubectl run -it --rm --restart=Never busybox --image=gcr.io/google-containers/busybox sh
사용하려는 파드가 이미 실행 중인 경우, 다음을 사용하여 명령을 실행할 수 있다.
kubectl exec <POD-NAME> -c <CONTAINER-NAME> -- <COMMAND>
연습을 위해, 파드를 몇 개 실행한다. 자신이 관리하는 서비스를 디버깅하는 경우에는 세부 사항을 상황에 맞게 변경하고, 아니면 아래의 과정을 그대로 수행하여 두 번째 데이터 포인트를 얻을 수 있다.
kubectl create deployment hostnames --image=registry.k8s.io/serve_hostname
deployment.apps/hostnames created
kubectl
명령어는 생성되거나 변경된 리소스의 유형과 이름을 출력하여, 여기서 출력된 리소스 유형과 이름은 다음 명령어에 사용할 수 있다.
디플로이먼트의 레플리카를 3개로 확장한다.
kubectl scale deployment hostnames --replicas=3
deployment.apps/hostnames scaled
참고로, 위의 명령들을 실행하여 디플로이먼트를 실행하는 것은 다음과 같은 YAML을 사용하는 것과 동일하다.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: hostnames
name: hostnames
spec:
selector:
matchLabels:
app: hostnames
replicas: 3
template:
metadata:
labels:
app: hostnames
spec:
containers:
- name: hostnames
image: registry.k8s.io/serve_hostname
"app" 레이블은 kubectl create deployment
명령에 의해 디플로이먼트의 이름으로 자동으로
설정된다.
파드가 실행 중인지 확인할 수 있다.
kubectl get pods -l app=hostnames
NAME READY STATUS RESTARTS AGE
hostnames-632524106-bbpiw 1/1 Running 0 2m
hostnames-632524106-ly40y 1/1 Running 0 2m
hostnames-632524106-tlaok 1/1 Running 0 2m
파드가 서빙 중인지도 확인할 수 있다. 파드 IP주소의 목록을 가져온 뒤 이를 직접 테스트할 수 있다.
kubectl get pods -l app=hostnames \
-o go-template='{{range .items}}{{.status.podIP}}{{"\n"}}{{end}}'
10.244.0.5
10.244.0.6
10.244.0.7
연습에 사용된 컨테이너 예제는 포트 9376에서 HTTP를 통해 호스트이름을 리턴하지만, 본인의 앱을 디버깅하는 경우, 포트 번호를 파드가 수신 대기 중인 포트 번호로 대체하면 된다.
파드 내에서 다음을 실행한다.
for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do
wget -qO- $ep
done
다음과 같은 결과가 출력될 것이다.
hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok
이 단계에서 예상한 응답을 받지 못한 경우, 파드가
정상적이지 않거나 또는 예상했던 포트에서 수신 대기를 하지 않고 있을 가능성이 있다.
어떤 일이 발생하고 있는지 확인하는 데 kubectl logs
가 유용할 수 있으며,
또는 파드에 직접 kubectl exec
를 실행하고
이를 통해 디버그을 수행해야 할 수도 있다.
지금까지 모든 것이 계획대로 진행되었다면, 서비스가 작동하지 않는 이유를 분석해 볼 수 있다.
여기까지 따라왔다면 아직 실제로 서비스를 생성하지 않았다는 것을 알아차렸을 것이다. 이는 의도적인 것이다. 이 단계는 때때로 잊어버리지만, 가장 먼저 확인해야 할 단계이다.
존재하지 않는 서비스에 액세스하려고 하면 어떤 일이 발생할까? 이름을 이용하여 이 서비스를 사용하는 다른 파드가 있다면 다음과 같은 결과를 얻을 것이다.
wget -O- hostnames
Resolving hostnames (hostnames)... failed: Name or service not known.
wget: unable to resolve host address 'hostnames'
가장 먼저 확인해야 할 것은 서비스가 실제로 존재하는지 여부이다.
kubectl get svc hostnames
No resources found.
Error from server (NotFound): services "hostnames" not found
이제 서비스를 생성하자. 이전에도 언급했듯이, 이것은 연습을 위한 예시이다. 본인의 서비스의 세부 사항을 여기에 입력할 수도 있다.
kubectl expose deployment hostnames --port=80 --target-port=9376
service/hostnames exposed
다시 조회해 본다.
kubectl get svc hostnames
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hostnames ClusterIP 10.0.1.175 <none> 80/TCP 5s
이제 서비스가 존재하는 것을 확인할 수 있다.
위와 마찬가지로, 위의 명령들을 실행하여 서비스를 실행하는 것은 다음과 같은 YAML을 사용하는 것과 동일하다.
apiVersion: v1
kind: Service
metadata:
labels:
app: hostnames
name: hostnames
spec:
selector:
app: hostnames
ports:
- name: default
protocol: TCP
port: 80
targetPort: 9376
환경설정의 전체 범위를 강조하기 위해, 여기에서 생성한 서비스는 파드와 다른 포트 번호를 사용한다. 실제 많은 서비스에서, 이러한 값은 동일할 수 있다.
hostnames-*
파드로 들어오는 트래픽에 영향을 줄 수 있는 네트워크 폴리시 인그레스 규칙을
배포하는 경우, 이를 검토해야 한다.
자세한 내용은 Network Policies를 참고한다.
클라이언트가 서비스를 사용하는 가장 일반적인 방법 중 하나는 DNS 이름을 통하는 것이다.
동일한 네임스페이스안의 파드 안에서, 다음을 실행한다.
nslookup hostnames
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: hostnames
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local
이것이 실패하면, 파드와 서비스가 다른 네임스페이스에 있는 경우일 수 있다. 네임스페이스를 지정한 이름(namespace-qualified name)을 사용해 본다(다시 말하지만, 파드 안에서 다음을 실행한다).
nslookup hostnames.default
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: hostnames.default
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local
이 방법이 성공했다면, 교차 네임스페이스 이름을 사용하기 위해 앱을 조정하거나, 또는 앱과 서비스를 동일한 네임스페이스에서 실행한다. 여전히 실패한다면, 완전히 지정된 이름(fully-qualified name)을 사용한다.
nslookup hostnames.default.svc.cluster.local
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: hostnames.default.svc.cluster.local
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local
여기서 접미사: "default.svc.cluster.local"에 유의한다. "default"는 작업 중인 네임스페이스이다. "svc"는 서비스임을 나타낸다. "cluster.local"은 클러스터 도메인을 나타내며, 클러스터마다 다를 수 있다.
동일한 작업을 클러스터의 노드에서 시도해 볼 수도 있다.
nslookup hostnames.default.svc.cluster.local 10.0.0.10
Server: 10.0.0.10
Address: 10.0.0.10#53
Name: hostnames.default.svc.cluster.local
Address: 10.0.1.175
완전히 지정된 이름은 조회가 가능하지만 상대적인 이름은 조회를 할 수 없는 경우,
파드의 /etc/resolv.conf
파일이 올바른지 확인해야 한다.
파드 내에서 다음을 실행한다.
cat /etc/resolv.conf
다음과 같은 내용이 표시될 것이다.
nameserver 10.0.0.10
search default.svc.cluster.local svc.cluster.local cluster.local example.com
options ndots:5
nameserver
라인은 클러스터의 DNS 서비스를 나타내야 한다.
이는 --cluster-dns
플래그와 함께 kubelet
으로 전달된다.
search
라인은 서비스 이름을 찾을 수 있는 적절한 접미사가 포함되어야 한다.
위 예시의 경우, ("default.svc.cluster.local" ->) 로컬 네임스페이스의 서비스,
("svc.cluster.local" ->) 모든 네임스페이스의 서비스,
마지막으로 클러스터 ("cluster.local" ->) 클러스터 범위에서 이름을 찾는다.
클러스터의 구성에 따라 그 뒤에 추가 레코드를 기입할 수도 있다(총 6개까지).
클러스터 접미사는 --cluster-domain
플래그와 함께
kubelet
에 전달된다. 이 문서에서, 클러스터 접미사는
"cluster.local"로 간주된다. 당신의 클러스터가 다르게 구성되었을 수도 있으며,
이 경우 이전의 모든 명령어에서 이를
변경해야 한다.
options
라인의 ndots
값은 DNS 클라이언트 라이브러리가 모든 탐색 경우의 수를 고려할 수 있을 만큼 충분히 높게 설정되어야 한다.
쿠버네티스는 기본적으로 5로 설정하며,
이는 쿠버네티스가 생성하는 모든 DNS 이름을 포함할 수 있을 만큼 충분히 높다.
위의 작업이 계속 실패하면, 서비스에 대해 DNS 조회가 작동하지 않는 것이다. 한 걸음 뒤로 물러나서 어떤 것이 작동하지 않는지 확인할 수 있다. 쿠버네티스 마스터 서비스는 항상 작동해야 한다. 파드 내에서 다음을 실행한다.
nslookup kubernetes.default
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: kubernetes.default
Address 1: 10.0.0.1 kubernetes.default.svc.cluster.local
이것이 실패하면, 이 문서의 kube-proxy 섹션을 참고하거나 본 문서의 맨 위로 돌아가서 다시 시작한다. 단, 서비스를 디버깅하는 대신, DNS 서비스를 디버그한다.
DNS가 작동하는 것을 확인했다고 가정하면, 다음 테스트는
서비스가 IP 주소로 작동하는지 확인하는 것이다. 클러스터의 파드에서,
서비스의 IP에 액세스한다(위와 같이 kubectl get
을 사용).
for i in $(seq 1 3); do
wget -qO- 10.0.1.175:80
done
이렇게 하면 다음과 같은 결과가 나타난다.
hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok
서비스가 작동하는 경우, 올바른 응답을 받았을 것이다. 그렇지 않은 경우, 잘못되어 있을 수 있는 여러가지 사항이 있다. 아래의 내용을 계속 읽어 본다.
몇 차례 강조하지만, 서비스가 정확하게 기재되어 있고 파드의 포트와 일치하는지 두 번, 세 번 확인해야 한다. 아래의 명령을 실행하여 서비스를 다시 조회해 본다.
kubectl get service hostnames -o json
{
"kind": "Service",
"apiVersion": "v1",
"metadata": {
"name": "hostnames",
"namespace": "default",
"uid": "428c8b6c-24bc-11e5-936d-42010af0a9bc",
"resourceVersion": "347189",
"creationTimestamp": "2015-07-07T15:24:29Z",
"labels": {
"app": "hostnames"
}
},
"spec": {
"ports": [
{
"name": "default",
"protocol": "TCP",
"port": 80,
"targetPort": 9376,
"nodePort": 0
}
],
"selector": {
"app": "hostnames"
},
"clusterIP": "10.0.1.175",
"type": "ClusterIP",
"sessionAffinity": "None"
},
"status": {
"loadBalancer": {}
}
}
spec.ports[]
에 나열되어 있는가?targetPort
가 올바른가(일부 파드는 서비스와 다른 포트를 사용한다)?여기까지 왔다면, 서비스가 올바르게 정의되어 있고 DNS에 의해 해석될 수 있음을 확인한 것이다. 이제 실행 중인 파드가 서비스에 의해 실제로 선택되고 있는지 확인한다.
이전에 파드가 실행 중임을 확인했다. 다음 명령을 통해 다시 확인할 수 있다.
kubectl get pods -l app=hostnames
NAME READY STATUS RESTARTS AGE
hostnames-632524106-bbpiw 1/1 Running 0 1h
hostnames-632524106-ly40y 1/1 Running 0 1h
hostnames-632524106-tlaok 1/1 Running 0 1h
-l app=hostnames
인수는 서비스에 구성된 레이블 셀렉터이다.
"AGE" 열은 파드가 실행된 지 약 1시간 되었다고 표시하는 것으로, 이는 파드가 제대로 실행되고 있으며 충돌하지 않음을 의미한다.
"RESTARTS" 열은 파드가 자주 충돌하지 않거나 다시 시작되지 않음을 나타낸다. 잦은 재시작은 간헐적인 연결 문제를 발생할 수 있다. 재시작 횟수가 높으면, 파드를 디버깅하는 방법에 대해 참고한다.
쿠버네티스 시스템 내부에는 모든 서비스의 셀렉터를 평가하고 그 결과를 해당 엔드포인트 오브젝트에 저장하는 제어 루프가 있다.
kubectl get endpoints hostnames
NAME ENDPOINTS
hostnames 10.244.0.5:9376,10.244.0.6:9376,10.244.0.7:9376
위의 결과를 통해 엔드포인트 컨트롤러가 서비스에 대한 올바른 파드를 찾았음을 알 수 있다.
ENDPOINTS
열이 <none>
인 경우,
서비스의 spec.selector
필드가 파드의 metadata.labels
값을
실제로 선택하는지 확인해야 한다.
흔한 실수로, kubectl run
명령으로 디플로이먼트도 생성할 수 있었던 1.18 이전 버전에서,
서비스는 app=hostnames
를 이용하여 파드를 선택하지만
디플로이먼트는 run=hostnames
를 이용하여 파드를 선택하는 것과 같은 오타, 또는 기타 오류가 있을 수 있다.
여기까지 왔다면, 서비스가 존재하며 이 서비스가 파드를 선택하고 있는 것이다. 파드 자체에 대해서는 이 연습의 시작부분에서 확인했었다. 이제 파드가 실제로 작동하는지 다시 확인해 보자. 위에서 나열된 엔드포인트를 이용하여 서비스 메카니즘을 우회하고 파드에 직접 접근할 수 있다.
파드 내에서 다음을 실행한다.
for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do
wget -qO- $ep
done
이렇게 하면 다음과 같은 결과가 나타난다.
hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok
엔드포인트 목록의 각 파드가 호스트네임을 반환할 것이라고 예상할 수 있다. 파드가 호스트네임을 반환하지 않는다면 (또는 파드가 정상 동작을 하지 않는다면) 무슨 일이 일어나고 있는지 조사해야 한다.
여기까지 진행했다면, 서비스가 실행 중이고, 엔드포인트가 있으며, 파드가 실제로 서빙하고 있는 것이다. 이 시점에서는, 전체 서비스 프록시 메커니즘이 의심된다. 하나씩 확인하기로 한다.
kube-proxy는 쿠버네티스 서비스의 기본 구현이며 대부분의 클러스터에서 사용하고 있다. kube-proxy는 모든 노드에서 실행되며, 서비스 추상화를 제공하기 위한 메커니즘 일부를 구성하는 프로그램이다. 사용자의 클러스터에서 kube-proxy를 사용하지 않는 경우, 다음 섹션이 적용되지 않으며, 사용 중인 서비스 구현에 대한 내용을 조사해야 할 것이다.
노드에서 kube-proxy
가 실행 중인지 확인한다. 다음의 명령을 노드에서 직접 실행하면,
아래와 같은 결과를 얻을 수 있다.
ps auxw | grep kube-proxy
root 4194 0.4 0.1 101864 17696 ? Sl Jul04 25:43 /usr/local/bin/kube-proxy --master=https://kubernetes-master --kubeconfig=/var/lib/kube-proxy/kubeconfig --v=2
다음으로, 반드시 되어야 하는 것(예: 마스터와 통신)이 잘 되는지를 확인한다.
이를 수행하려면, 로그를 확인한다. 로그에 액세스하는 방법은
노드의 OS 별로 다르다. 일부 OS에서는 /var/log/kube-proxy.log와 같은 파일이다.
반면 어떤 OS에서는 로그에 액세스하기 위해 journalctl
을 사용한다.
다음과 같은 내용이 표시될 것이다.
I1027 22:14:53.995134 5063 server.go:200] Running in resource-only container "/kube-proxy"
I1027 22:14:53.998163 5063 server.go:247] Using iptables Proxier.
I1027 22:14:54.038140 5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns-tcp" to [10.244.1.3:53]
I1027 22:14:54.038164 5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns" to [10.244.1.3:53]
I1027 22:14:54.038209 5063 proxier.go:352] Setting endpoints for "default/kubernetes:https" to [10.240.0.2:443]
I1027 22:14:54.038238 5063 proxier.go:429] Not syncing iptables until Services and Endpoints have been received from master
I1027 22:14:54.040048 5063 proxier.go:294] Adding new service "default/kubernetes:https" at 10.0.0.1:443/TCP
I1027 22:14:54.040154 5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns" at 10.0.0.10:53/UDP
I1027 22:14:54.040223 5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns-tcp" at 10.0.0.10:53/TCP
마스터와 통신할 수 없다는 오류 메시지가 표시되면 노드 구성 및 설치 단계를 다시 확인해야 한다.
kube-proxy
가 올바르게 실행되지 않는 이유 중 하나로
필수 conntrack
바이너리를 찾을 수 없는 경우가 있을 수 있다.
클러스터를 설치하는 방법(예, 사전에 준비 없이 쿠버네티스를 설치)에 따라
일부 리눅스 시스템에서 발생할 수 있다. 이 경우,
conntrack
패키지를 수동으로 설치(예: 우분투에서 sudo apt install conntrack
)한 다음
다시 시도해야 한다.
Kube-proxy는 몇 가지 모드 중 하나로 실행할 수 있다. 위에 나열된 로그에서,
Using iptables Proxier
라인은 kube-proxy가 "iptables" 모드로 실행 중임을 나타낸다.
가장 일반적인 다른 모드는 "ipvs"이다.
"iptables" 모드일 때, 노드에서 다음 명령을 실행하면 아래와 같은 내용이 표시될 것이다.
iptables-save | grep hostnames
-A KUBE-SEP-57KPRZ3JQVENLNBR -s 10.244.3.6/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-57KPRZ3JQVENLNBR -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.3.6:9376
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -s 10.244.1.7/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.1.7:9376
-A KUBE-SEP-X3P2623AGDH6CDF3 -s 10.244.2.3/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-X3P2623AGDH6CDF3 -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.2.3:9376
-A KUBE-SERVICES -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NWV5X2332I4OT4T3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-WNBA2IHDGP2BOBGZ
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-X3P2623AGDH6CDF3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -j KUBE-SEP-57KPRZ3JQVENLNBR
각 서비스의 포트에 대해, KUBE-SERVICES
내의 하나의 규칙, 그리고
하나의 KUBE-SVC-<hash>
체인이 있어야 한다. 각 파드 엔드포인트에 대해,
해당 KUBE-SVC-<hash>
에는 몇 개의 규칙이 있어야 하고, 또한 몇 개의 규칙을 포함하는 하나의
KUBE-SEP-<hash>
체인이 있어야 한다. 정확한 규칙은
사용자의 정확한 설정(노드 포트 및 로드 밸런서 포함)에 따라 다르다.
"ipvs" 모드일 때, 노드에서 다음 명령을 실행하면 아래와 같은 내용이 표시될 것이다.
ipvsadm -ln
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
...
TCP 10.0.1.175:80 rr
-> 10.244.0.5:9376 Masq 1 0 0
-> 10.244.0.6:9376 Masq 1 0 0
-> 10.244.0.7:9376 Masq 1 0 0
...
각 서비스의 포트와 모든 NodePort, 외부 IP
및 로드 밸런서 IP에 대해 kube-proxy는 가상 서버를 생성한다.
각 파드 엔드포인트에 대해, kube-proxy는 각 가상 서버에 대응되는 실제 서버를 생성한다.
예제에서, 'hostnames' 서비스(10.0.1.175:80
)에는 3개의 엔드포인트(10.244.0.5:9376
,
10.244.0.6:9376
, 10.244.0.7:9376
)가 있다.
위에서 소개한 사례 중 하나를 마주했다고 가정한다면, 노드 중 하나에서 다음을 실행하여 서비스에 IP로 다시 액세스해 본다.
curl 10.0.1.175:80
hostnames-632524106-bbpiw
그래도 실패한다면, kube-proxy
로그에서 다음과 같은 특정 라인을 확인한다.
Setting endpoints for default/hostnames:default to [10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376]
이러한 라인이 보이지 않으면, -v
플래그를 4로 설정하여 kube-proxy
를 재시작한 다음
로그를 다시 살펴본다.
이러한 일이 일어날 것 같지 않을 수도 있지만 실제로 일어나기도 하며, 정상적이라면 제대로 동작해야 한다.
이러한 현상은 네트워크가 '헤어핀' 트래픽(클러스터 내부에서 U턴하는 트래픽)에 대해 제대로 구성되지 않은 경우에 발생할 수 있으며,
이는 주로 kube-proxy
가 iptables
모드에서 실행되고
파드가 브릿지 네트워크에 연결된 경우에 해당할 수 있다.
kubelet
은 파드가 자신이 속한 서비스 VIP로 접근하는 경우
서비스의 엔드포인트가 이 트래픽을 다시 서비스의 파드로 로드밸런싱하도록 하는
'hairpin-mode[플래그](/docs/reference/command-line-tools-reference/kubelet/)를 노출한다.
hairpin-mode플래그는
hairpin-veth또는
promiscuous-bridge`로 설정되어야 한다.
이 문제를 해결하기 위한 일반적인 단계는 다음과 같다.
hairpin-mode
가 hairpin-veth
또는 promiscuous-bridge
로 설정되어 있는지 확인한다.
아래와 같은 내용이 표시되어야 한다. 아래 예시에서 hairpin-mode
는
promiscuous-bridge
로 설정되어 있다.ps auxw | grep kubelet
root 3392 1.1 0.8 186804 65208 ? Sl 00:51 11:11 /usr/local/bin/kubelet --enable-debugging-handlers=true --config=/etc/kubernetes/manifests --allow-privileged=True --v=4 --cluster-dns=10.0.0.10 --cluster-domain=cluster.local --configure-cbr0=true --cgroup-root=/ --system-cgroups=/system --hairpin-mode=promiscuous-bridge --runtime-cgroups=/docker-daemon --kubelet-cgroups=/kubelet --babysit-daemons=true --max-pods=110 --serialize-image-pulls=false --outofdisk-transition-frequency=0
hairpin-mode
가 무엇인지 확인한다. 이를 확인하려면, kubelet 로그를 확인해야 한다.
로그 액세스는 노드 OS에 따라 다르다. 일부 OS에서는
/var/log/kubelet.log와 같은 파일인 반면 다른 OS에서는 journalctl
을
사용하여 로그에 액세스한다. 실제로 적용되어 있는 hairpin 모드는 호환성으로 인해 --hairpin-mode
플래그와
일치하지 않을 수 있으므로 주의한다. kubelet.log에 hairpin
이라는 키워드가 포함된
로그 라인이 있는지 확인한다. 아래와 같이
실행 중인 헤어핀 모드를 나타내는 로그 라인이 있을 것이다.I0629 00:51:43.648698 3252 kubelet.go:380] Hairpin mode set to "promiscuous-bridge"
hairpin-veth
인 경우, kubelet
이
노드의 /sys
에서 작동할 수 있는 권한이 있는지 확인한다. 모든 것이 제대로 작동한다면,
다음 명령을 실행했을 때 아래와 같이 표시될 것이다.for intf in /sys/devices/virtual/net/cbr0/brif/*; do cat $intf/hairpin_mode; done
1
1
1
1
promiscuous-bridge
인 경우,
Kubelet
이 노드 안에서 리눅스 브릿지를 조작할 수 있는 권한이 있는지 확인한다. cbr0
브릿지가
사용되었고 제대로 구성되어 있다면, 다음 명령을 실행했을 때 아래와 같이 표시될 것이다.ifconfig cbr0 |grep PROMISC
UP BROADCAST RUNNING PROMISC MULTICAST MTU:1460 Metric:1
여기까지 왔다면, 아주 이상한 일이 발생하고 있는 것이다. 서비스가
실행 중이고, 엔드포인트가 있으며, 파드가 실제로 서빙하고 있는 상태이다.
DNS가 작동 중이고, kube-proxy
가 오작동하는 것은 아닌 것 같다.
그럼에도 서비스가 동작하지 않는 것이다.
우리가 도울 수 있도록, 어떤 일이 일어나고 있는지 커뮤니티에 알려주세요!
자세한 내용은 트러블슈팅하기 개요 문서 를 참고한다.
이 문서에서는 스테이트풀셋을 디버깅 방법에 대해 설명한다.
레이블이 app.kubernetes.io/name=MyApp
으로 지정된 스테이트풀셋 파드를 전부 나열하기 위해서는
다음의 명령을 사용할 수 있다.
kubectl get pods -l app.kubernetes.io/name=MyApp
만약 오랜 시간동안 Unknown
이나 Terminating
상태에 있는
파드들을 발견하였다면, 이러한 파드들을 어떻게 다루는지 알아보기 위해
스테이트풀셋 파드 삭제하기를 참고하길 바란다.
스테이트풀셋에 포함된 개별 파드들을 디버깅하기 위해서는
파드 디버그하기 가이드를 참고하길 바란다.
초기화 컨테이너(Init container) 디버그하기를 참고하길 바란다.
이 페이지는 컨테이너 종료 메시지를 읽고 쓰는 방법을 보여준다.
종료 메시지는 컨테이너가 치명적인 이벤트에 대한 정보를, 대시보드나 모니터링 소프트웨어 도구와 같이 쉽게 조회 및 표시할 수 있는 위치에 기록하는 방법을 제공한다. 대부분의 경우에 종료 메시지에 넣는 정보는 일반적으로 쿠버네티스 로그에도 쓰여져야 한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
이 예제에서는, 하나의 컨테이너를 실행하는 파드를 생성한다. 파드의 매니페스트는 컨테이너가 시작될 때 수행하는 명령어를 지정한다.
apiVersion: v1
kind: Pod
metadata:
name: termination-demo
spec:
containers:
- name: termination-demo-container
image: debian
command: ["/bin/sh"]
args: ["-c", "sleep 10 && echo Sleep expired > /dev/termination-log"]
다음의 YAML 설정 파일에 기반한 파드를 생성한다.
kubectl apply -f https://k8s.io/examples/debug/termination.yaml
YAML 파일에 있는 command
와 args
필드에서 컨테이너가 10초 간 잠든 뒤에
"Sleep expired" 문자열을 /dev/termination-log
파일에 기록하는
것을 확인할 수 있다. 컨테이너는 "Sleep expired" 메시지를
기록한 후에 종료된다.
파드와 관련된 정보를 출력한다.
kubectl get pod termination-demo
파드가 더 이상 실행되지 않을 때까지 앞선 명령어를 반복한다.
파드에 관한 상세 정보를 출력한다.
kubectl get pod termination-demo --output=yaml
결과는 "Sleep expired" 메시지를 포함한다.
apiVersion: v1
kind: Pod
...
lastState:
terminated:
containerID: ...
exitCode: 0
finishedAt: ...
message: |
Sleep expired
...
종료 메시지만을 포함하는 출력 결과를 보기 위해서는 Go 템플릿을 사용한다.
kubectl get pod termination-demo -o go-template="{{range .status.containerStatuses}}{{.lastState.terminated.message}}{{end}}"
여러 컨테이너를 포함하는 파드의 경우, Go 템플릿을 사용하여 컨테이너 이름도 출력할 수 있다. 이렇게 하여, 어떤 컨테이너가 실패하는지 찾을 수 있다.
kubectl get pod multi-container-pod -o go-template='{{range .status.containerStatuses}}{{printf "%s:\n%s\n\n" .name .lastState.terminated.message}}{{end}}'
쿠버네티스는 컨테이너의 terminationMessagePath
필드에 지정된
종료 메시지 파일에서 종료 메시지를 검색하며, 이 필드의 기본값은
/dev/termination-log
이다. 이 필드를 사용자 정의 함으로써
쿠버네티스가 종료 메시지를 검색할 때 다른 파일을 사용하도록 조정할 수 있다.
쿠버네티스는 지정된 파일의 내용을 사용하여 컨테이너의 성공 및 실패에 대한 상태 메시지를 채운다.
종료 메시지는 assertion failure 메세지처럼 간결한 최종 상태로 생성된다. kubelet은 4096 바이트보다 긴 메시지를 자른다.
모든 컨테이너의 총 메시지 길이는 12KiB로 제한되며, 각 컨테이너에 균등하게 분할된다.
예를 들어, 12개의 컨테이너(initContainers
또는 containers
)가 있는 경우 각 컨테이너에는 1024 바이트의 사용 가능한 종료 메시지 공간이 있다.
기본 종료 메시지 경로는 /dev/termination-log
이다.
파드가 시작된 후에는 종료 메시지 경로를 설정할 수 없다.
다음의 예제에서 컨테이너는, 쿠버네티스가 조회할 수 있도록
/tmp/my-log
파일에 종료 메시지를 기록한다.
apiVersion: v1
kind: Pod
metadata:
name: msg-path-demo
spec:
containers:
- name: msg-path-demo-container
image: debian
terminationMessagePath: "/tmp/my-log"
또한 사용자는 추가적인 사용자 정의를 위해 컨테이너의 terminationMessagePolicy
필드를 설정할 수 있다. 이 필드의 기본 값은 File
이며,
이는 오직 종료 메시지 파일에서만 종료 메시지가 조회되는 것을 의미한다.
terminationMessagePolicy
필드의 값을 "FallbackToLogsOnError
으로
설정함으로써, 종료 메시지 파일이 비어 있고 컨테이너가 오류와 함께 종료 되었을 경우
쿠버네티스가 컨테이너 로그 출력의 마지막 청크를 사용하도록 지시할 수 있다.
로그 출력은 2048 바이트나 80 행 중 더 작은 값으로 제한된다.
이 페이지는 초기화 컨테이너의 실행과 관련된 문제를
조사하는 방법에 대해 보여준다. 아래 예제의 커맨드 라인은 파드(Pod)를 <pod-name>
으로,
초기화 컨테이너를 <init-container-1>
과
<init-container-2>
로 표시한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
사용자 파드의 상태를 표시한다.
kubectl get pod <pod-name>
예를 들어, Init:1/2
상태는 두 개의 초기화 컨테이너 중
하나가 성공적으로 완료되었음을 나타낸다.
NAME READY STATUS RESTARTS AGE
<pod-name> 0/1 Init:1/2 0 7s
상태값과 그 의미에 대한 추가 예제는 파드 상태 이해하기를 참조한다.
초기화 컨테이너의 실행에 대한 상세 정보를 확인한다.
kubectl describe pod <pod-name>
예를 들어, 2개의 초기화 컨테이너가 있는 파드는 다음과 같이 표시될 수 있다.
Init Containers:
<init-container-1>:
Container ID: ...
...
State: Terminated
Reason: Completed
Exit Code: 0
Started: ...
Finished: ...
Ready: True
Restart Count: 0
...
<init-container-2>:
Container ID: ...
...
State: Waiting
Reason: CrashLoopBackOff
Last State: Terminated
Reason: Error
Exit Code: 1
Started: ...
Finished: ...
Ready: False
Restart Count: 3
...
파드 스펙의 status.initContainerStatuses
필드를 읽어서
프로그래밍 방식으로 초기화 컨테이너의 상태를 조회할 수도 있다.
kubectl get pod nginx --template '{{.status.initContainerStatuses}}'
이 명령은 원시 JSON 방식으로 위와 동일한 정보를 반환한다.
초기화 컨테이너의 로그를 확인하기 위해 파드의 이름과 초기화 컨테이너의 이름을 같이 전달한다.
kubectl logs <pod-name> -c <init-container-2>
셸 스크립트를 실행하는 초기화 컨테이너는, 초기화 컨테이너가
실행될 때 명령어를 출력한다. 예를 들어, 스크립트의 시작 부분에
set -x
를 추가하고 실행하여 Bash에서 명령어를 출력할 수 있도록 수행할 수 있다.
Init:
으로 시작하는 파드 상태는 초기화 컨테이너의
실행 상태를 요약한다. 아래 표는 초기화 컨테이너를 디버깅하는
동안 사용자가 확인할 수 있는 몇 가지 상태값의 예이다.
상태 | 의미 |
---|---|
Init:N/M |
파드가 M 개의 초기화 컨테이너를 갖고 있으며, 현재까지 N 개가 완료. |
Init:Error |
초기화 컨테이너 실행 실패. |
Init:CrashLoopBackOff |
초기화 컨테이너가 반복적으로 실행 실패. |
Pending |
파드가 아직 초기화 컨테이너를 실행하지 않음. |
PodInitializing or Running |
파드가 이미 초기화 컨테이너 실행을 완료. |
이 페이지는 노드에서 동작 중인(혹은 크래시된) 파드를 디버그하는 방법에 대해 설명한다.
kubectl
을 사용하는 일반적인 디버깅 과정에서는 이러한 접근 권한이 필요하지 않다.kubectl describe pod
명령으로 파드 상세사항 가져오기이 예제에서는 앞의 예제와 비슷하게 두 개의 파드를 생성하기 위해 디플로이먼트를 사용할 것이다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
다음 명령을 실행하여 디플로이먼트를 생성한다.
kubectl apply -f https://k8s.io/examples/application/nginx-with-request.yaml
deployment.apps/nginx-deployment created
다음 명령을 실행하여 파드 상태를 확인한다.
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-67d4bdd6f5-cx2nz 1/1 Running 0 13s
nginx-deployment-67d4bdd6f5-w6kd7 1/1 Running 0 13s
다음과 같이 kubectl describe pod
명령을 사용하여 각 파드에 대한 더 많은 정보를 가져올 수 있다.
kubectl describe pod nginx-deployment-67d4bdd6f5-w6kd7
Name: nginx-deployment-67d4bdd6f5-w6kd7
Namespace: default
Priority: 0
Node: kube-worker-1/192.168.0.113
Start Time: Thu, 17 Feb 2022 16:51:01 -0500
Labels: app=nginx
pod-template-hash=67d4bdd6f5
Annotations: <none>
Status: Running
IP: 10.88.0.3
IPs:
IP: 10.88.0.3
IP: 2001:db8::1
Controlled By: ReplicaSet/nginx-deployment-67d4bdd6f5
Containers:
nginx:
Container ID: containerd://5403af59a2b46ee5a23fb0ae4b1e077f7ca5c5fb7af16e1ab21c00e0e616462a
Image: nginx
Image ID: docker.io/library/nginx@sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767
Port: 80/TCP
Host Port: 0/TCP
State: Running
Started: Thu, 17 Feb 2022 16:51:05 -0500
Ready: True
Restart Count: 0
Limits:
cpu: 500m
memory: 128Mi
Requests:
cpu: 500m
memory: 128Mi
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-bgsgp (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-bgsgp:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: Guaranteed
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 34s default-scheduler Successfully assigned default/nginx-deployment-67d4bdd6f5-w6kd7 to kube-worker-1
Normal Pulling 31s kubelet Pulling image "nginx"
Normal Pulled 30s kubelet Successfully pulled image "nginx" in 1.146417389s
Normal Created 30s kubelet Created container nginx
Normal Started 30s kubelet Started container nginx
위 예시에서 컨테이너와 파드에 대한 구성 정보(레이블, 리소스 요구사항 등) 및 상태 정보(상태(state), 준비성(readiness), 재시작 횟수, 이벤트 등)를 볼 수 있다.
컨테이너의 상태(state)값은 Waiting, Running, 또는 Terminated 중 하나이다. 각 상태에 따라, 추가 정보가 제공될 것이다. 위 예시에서 Running 상태의 컨테이너에 대해서는 컨테이너의 시작 시각을 시스템이 표시해 주는 것을 볼 수 있다.
Ready 값은 컨테이너의 마지막 준비성 프로브(readiness probe) 통과 여부를 알려 준다. (위 예시에서는 컨테이너에 준비성 프로브가 설정되어 있지 않다. 컨테이너에 준비성 프로브가 설정되어 있지 않으면, 컨테이너는 준비(ready) 상태로 간주된다.)
'재시작 카운트'는 컨테이너가 재시작된 횟수를 보여 준다. 이 정보는 재시작 정책이 'always'로 설정된 컨테이너의 반복적인 강제 종료를 알아차리는 데에 유용하다.
위 예시에서 파드와 연관된 유일한 컨디션(Condition)은 True 또는 False 값을 갖는 Ready 컨디션이며, 이 값이 True라는 것은 파드가 요청을 처리할 수 있으며 모든 동일한 서비스를 묶는 로드 밸런싱 풀에 추가되어야 함을 의미한다.
마지막으로, 파드와 관련된 최근 이벤트 로그가 표시된다. 시스템은 동일한 여러 이벤트를 처음/마지막 발생 시간 및 발생 횟수만 압축적으로 표시한다. "From"은 이벤트 로그를 발생하는 구성 요소를 가리키고, "SubobjectPath"는 참조되는 개체(예: 파드 내 컨테이너)를 나타내며, "Reason" 및 "Message"는 발생한 상황을 알려 준다.
이벤트를 사용하여 감지할 수 있는 일반적인 시나리오는 노드에 할당될 수 없는 파드를 생성하는 경우이다. 예를 들어 파드가 노드에 사용 가능한 리소스보다 더 많은 리소스를 요청하거나, 또는 어떤 노드에도 해당되지 않는 레이블 셀렉터를 명시했을 수 있다. 예를 들어 4개 노드로 구성되며 각 (가상) 머신에 1 CPU가 있는 클러스터가 있는 상황에서, 위 예시 대신 2 레플리카가 아니라 5 레플리카를, 500 밀리코어가 아니라 600 밀리코어를 요청하는 디플로이먼트를 배포했다고 해 보자. 이러한 경우 5개의 파드 중 하나는 스케줄링될 수 없을 것이다. (각 노드에는 fluentd, skydns 등의 클러스터 애드온도 실행되고 있으므로, 만약 1000 밀리코어를 요청했다면 파드가 하나도 스케줄될 수 없었을 것이다.)
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-1006230814-6winp 1/1 Running 0 7m
nginx-deployment-1006230814-fmgu3 1/1 Running 0 7m
nginx-deployment-1370807587-6ekbw 1/1 Running 0 1m
nginx-deployment-1370807587-fg172 0/1 Pending 0 1m
nginx-deployment-1370807587-fz9sd 0/1 Pending 0 1m
nginx-deployment-1370807587-fz9sd 파드가 왜 실행되지 않는지를 알아 보려면, pending 상태의 파드에 대해 kubectl describe pod
명령을 실행하고 이벤트(event) 항목을 확인해 볼 수 있다.
kubectl describe pod nginx-deployment-1370807587-fz9sd
Name: nginx-deployment-1370807587-fz9sd
Namespace: default
Node: /
Labels: app=nginx,pod-template-hash=1370807587
Status: Pending
IP:
Controllers: ReplicaSet/nginx-deployment-1370807587
Containers:
nginx:
Image: nginx
Port: 80/TCP
QoS Tier:
memory: Guaranteed
cpu: Guaranteed
Limits:
cpu: 1
memory: 128Mi
Requests:
cpu: 1
memory: 128Mi
Environment Variables:
Volumes:
default-token-4bcbi:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-4bcbi
Events:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
1m 48s 7 {default-scheduler } Warning FailedScheduling pod (nginx-deployment-1370807587-fz9sd) failed to fit in any node
fit failure on node (kubernetes-node-6ta5): Node didn't have enough resource: CPU, requested: 1000, used: 1420, capacity: 2000
fit failure on node (kubernetes-node-wul5): Node didn't have enough resource: CPU, requested: 1000, used: 1100, capacity: 2000
여기서 스케줄러가 기록한 이벤트를 통해, 파드가 FailedScheduling
사유로 인해 스케줄링되지 않았음을 알 수 있다(다른 이유도 있을 수 있음). 이 메시지를 통해 어떤 노드에도 이 파드를 실행하기 위한 충분한 리소스가 없었음을 알 수 있다.
이 상황을 바로잡으려면, kubectl scale
명령으로 디플로이먼트의 레플리카를 4 이하로 줄일 수 있다. (또는 한 파드를 pending 상태로 두어도 되며, 이렇게 해도 문제는 없다.)
kubectl describe pod
출력의 마지막에 있는 것과 같은 이벤트는 etcd에 기록되어 보존되며 클러스터에 어떤 일이 일어나고 있는지에 대한 높은 차원의 정보를 제공한다. 모든 이벤트의 목록을 보려면 다음 명령을 실행한다.
kubectl get events
그런데 이벤트는 네임스페이스 스코프 객체라는 것을 기억해야 한다. 즉 네임스페이스 스코프 객체에 대한 이벤트(예: my-namespace
네임스페이스의 파드에 어떤 일이 발생했는지)가 궁금하다면, 다음과 같이 커맨드에 네임스페이스를 명시해야 한다.
kubectl get events --namespace=my-namespace
모든 네임스페이스에 대한 이벤트를 보려면, --all-namespaces
인자를 사용할 수 있다.
kubectl describe pod
명령 외에도, kubectl get pod
이상의 정보를 얻는 다른 방법은 kubectl get pod
명령에 출력 형식 플래그 -o yaml
인자를 추가하는 것이다. 이렇게 하면 kubectl describe pod
명령보다 더 많은 정보, 원천적으로는 시스템이 파드에 대해 알고 있는 모든 정보를 YAML 형식으로 볼 수 있다. 여기서 어노테이션(레이블 제한이 없는 키-밸류 메타데이터이며, 쿠버네티스 시스템 구성 요소가 내부적으로 사용함), 재시작 정책, 포트, 볼륨과 같은 정보를 볼 수 있을 것이다.
kubectl get pod nginx-deployment-1006230814-6winp -o yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2022-02-17T21:51:01Z"
generateName: nginx-deployment-67d4bdd6f5-
labels:
app: nginx
pod-template-hash: 67d4bdd6f5
name: nginx-deployment-67d4bdd6f5-w6kd7
namespace: default
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: nginx-deployment-67d4bdd6f5
uid: 7d41dfd4-84c0-4be4-88ab-cedbe626ad82
resourceVersion: "1364"
uid: a6501da1-0447-4262-98eb-c03d4002222e
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: nginx
ports:
- containerPort: 80
protocol: TCP
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 500m
memory: 128Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-bgsgp
readOnly: true
dnsPolicy: ClusterFirst
enableServiceLinks: true
nodeName: kube-worker-1
preemptionPolicy: PreemptLowerPriority
priority: 0
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: default
serviceAccountName: default
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:
- name: kube-api-access-bgsgp
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
status:
conditions:
- lastProbeTime: null
lastTransitionTime: "2022-02-17T21:51:01Z"
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: "2022-02-17T21:51:06Z"
status: "True"
type: Ready
- lastProbeTime: null
lastTransitionTime: "2022-02-17T21:51:06Z"
status: "True"
type: ContainersReady
- lastProbeTime: null
lastTransitionTime: "2022-02-17T21:51:01Z"
status: "True"
type: PodScheduled
containerStatuses:
- containerID: containerd://5403af59a2b46ee5a23fb0ae4b1e077f7ca5c5fb7af16e1ab21c00e0e616462a
image: docker.io/library/nginx:latest
imageID: docker.io/library/nginx@sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767
lastState: {}
name: nginx
ready: true
restartCount: 0
started: true
state:
running:
startedAt: "2022-02-17T21:51:05Z"
hostIP: 192.168.0.113
phase: Running
podIP: 10.88.0.3
podIPs:
- ip: 10.88.0.3
- ip: 2001:db8::1
qosClass: Guaranteed
startTime: "2022-02-17T21:51:01Z"
먼저, 확인하고자 하는 컨테이너의 로그를 확인한다.
kubectl logs ${POD_NAME} ${CONTAINER_NAME}
만약 컨테이너가 이전에 크래시 되었다면, 다음의 명령을 통해 컨테이너의 크래시 로그를 살펴볼 수 있다.
kubectl logs --previous ${POD_NAME} ${CONTAINER_NAME}
만약 컨테이너 이미지에
디버깅 도구가 포함되어 있다면, kubectl exec
을 통해 특정 컨테이너에서 해당 명령들을
실행할 수 있다. (리눅스나 윈도우 OS를 기반으로 만들어진 이미지에는 대부분 디버깅 도구를 포함하고
있다.)
kubectl exec ${POD_NAME} -c ${CONTAINER_NAME} -- ${CMD} ${ARG1} ${ARG2} ... ${ARGN}
-c ${CONTAINER_NAME}
인자는 선택적이다. 만약 하나의 컨테이너만 포함된 파드라면 해당 옵션을 생략할 수 있다.예를 들어, 동작 중인 카산드라 파드의 로그를 살펴보기 위해서는 다음과 같은 명령을 실행할 수 있다.
kubectl exec cassandra -- cat /var/log/cassandra/system.log
kubectl exec
에 -i
와 -t
옵션을 사용해서 터미널에서 접근할 수 있는 쉘을 실행시킬 수도 있다.
예를 들면 다음과 같다.
kubectl exec -it cassandra -- sh
더욱 상세한 내용은 동작중인 컨테이너의 쉘에 접근하기를 참고한다.
Kubernetes v1.25 [stable]
컨테이너가 크래시 됐거나
distroless 이미지처럼
컨테이너 이미지에 디버깅 도구를 포함하고 있지 않아 kubectl exec
로는 충분하지 않은 경우에는
임시(Ephemeral) 컨테이너를 사용하는 것이
인터랙티브한 트러블슈팅에 유용하다.
kubectl debug
명령어를 사용해서 동작 중인 파드에 임시 컨테이너를 추가할 수 있다.
먼저, 다음과 같이 파드를 추가한다.
kubectl run ephemeral-demo --image=registry.k8s.io/pause:3.1 --restart=Never
이 섹션의 예시에서는 디버깅 도구가 포함되지 않은 이미지의 사례를 보여드리기 위해
pause
컨테이너 이미지를 사용했는데, 이 대신 어떠한 이미지를 사용해도
될 것이다.
만약 kubectl exec
을 통해 쉘을 생성하려 한다면 다음과 같은 에러를
확인할 수 있을 텐데, 그 이유는 이 이미지에 쉘이 존재하지 않기 때문이다.
kubectl exec -it ephemeral-demo -- sh
OCI runtime exec failed: exec failed: container_linux.go:346: starting container process caused "exec: \"sh\": executable file not found in $PATH": unknown
이 명령어 대신 kubectl debug
을 사용해서 디버깅 컨테이너를 생성할 수 있다.
만약 -i
/--interactive
인자를 사용한다면, kubectl
은 임시
컨테이너의 콘솔에 자동으로 연결할 것이다.
kubectl debug -it ephemeral-demo --image=busybox --target=ephemeral-demo
Defaulting debug container name to debugger-8xzrl.
If you don't see a command prompt, try pressing enter.
/ #
이 명령어는 새로운 busybox 컨테이너를 추가하고 해당 컨테이너로 연결한다. --target
파라미터를 사용하면 다른 컨테이너의 프로세스 네임스페이스를 대상으로 하게 된다. 여기서는
이 옵션이 꼭 필요한데, kubectl run
이 생성하는 파드에 대해
프로세스 네임스페이스 공유를
활성화하지 않기 때문이다.
--target
파라미터는 사용 중인
컨테이너 런타임에서
지원해야지만 사용할 수 있다. 만일 지원되지 않는다면,
임시 컨테이너가 시작되지 않을 수 있거나 독립적인 프로세스
네임스페이스를 가지고 시작될 수 있다.kubectl describe
명령을 사용하면 새롭게 생성된 임시 컨테이너의 상태를 확인할 수 있다.
kubectl describe pod ephemeral-demo
...
Ephemeral Containers:
debugger-8xzrl:
Container ID: docker://b888f9adfd15bd5739fefaa39e1df4dd3c617b9902082b1cfdc29c4028ffb2eb
Image: busybox
Image ID: docker-pullable://busybox@sha256:1828edd60c5efd34b2bf5dd3282ec0cc04d47b2ff9caa0b6d4f07a21d1c08084
Port: <none>
Host Port: <none>
State: Running
Started: Wed, 12 Feb 2020 14:25:42 +0100
Ready: False
Restart Count: 0
Environment: <none>
Mounts: <none>
...
디버깅이 다 끝나면 kubectl delete
을 통해 파드를 제거할 수 있다.
kubectl delete pod ephemeral-demo
때때로 파드의 설정 옵션에 따라 특정 상황에서 트러블슈팅을 하기가 어려울 수 있다.
예를 들어, 만일 여러분의 컨테이너 이미지가 쉘을 포함하고 있지 않거나, 여러분의
애플리케이션이 컨테이너 시작에서 크래시가 발생한다면 kubectl exec
을 이용해서
컨테이너를 트러블슈팅할 수 없을 수 있다. 이러한 상황에서는 kubectl debug
을 사용해서
파드의 복제본을 디버깅을 위한 추가적인 설정 옵션과 함께 생성할 수 있다.
만일 여러분의 애플리케이션이 동작은 하고 있지만 예상과는 다르게 동작하는 경우, 파드의 복제본에 새로운 컨테이너를 추가함으로써 추가적인 트러블슈팅 도구들을 파드에 함께 추가할 수 있다.
가령, 여러분의 애플리케이션 컨테이너 이미지는 busybox
를 기반으로 하고 있는데
여러분은 busybox
에는 없는 디버깅 도구를 필요로 한다고 가정해 보자. 이러한
시나리오는 kubectl run
명령을 통해 시뮬레이션 해볼 수 있다.
kubectl run myapp --image=busybox --restart=Never -- sleep 1d
다음의 명령을 실행시켜 디버깅을 위한 새로운 우분투 컨테이너와 함께 myapp-debug
이란
이름의 myapp
컨테이너 복제본을 생성할 수 있다.
kubectl debug myapp -it --image=ubuntu --share-processes --copy-to=myapp-debug
Defaulting debug container name to debugger-w7xmf.
If you don't see a command prompt, try pressing enter.
root@myapp-debug:/#
--container
플래그와 함께 지정하지 않는다면,
kubectl debug
는 자동으로 새로운 컨테이너 이름을 생성한다.-i
플래그를 사용하면 kubectl debug
명령이 새로운 컨테이너에 기본적으로 연결되게 된다.
이러한 동작은 --attach=false
을 지정하여 방지할 수 있다. 만일 여러분의 세션이
연결이 끊어진다면 kubectl attach
를 사용해서 다시 연결할 수 있다.--share-processes
옵션은 이 파드에 있는 컨테이너가 해당 파드에 속한 다른 컨테이너의
프로세스를 볼 수 있도록 한다. 이 옵션이 어떻게 동작하는지에 대해 더 알아보기 위해서는
다음의 파드의 컨테이너 간 프로세스 네임스페이스 공유를 참고하라.사용이 모두 끝나면, 디버깅에 사용된 파드를 잊지 말고 정리한다.
kubectl delete pod myapp myapp-debug
때때로 컨테이너의 명령어를 변경하는 것이 유용한 경우가 있는데, 예를 들면 디버그 플래그를 추가하기 위해서나 애플리케이션이 크래시 되는 경우이다.
다음의 kubectl run
명령을 통해 즉각적으로 크래시가 발생하는 애플리케이션의
사례를 시뮬레이션해 볼 수 있다.
kubectl run --image=busybox myapp -- false
kubectl describe pod myapp
명령을 통해 이 컨테이너에 크래시가 발생하고 있음을 확인할 수 있다.
Containers:
myapp:
Image: busybox
...
Args:
false
State: Waiting
Reason: CrashLoopBackOff
Last State: Terminated
Reason: Error
Exit Code: 1
이러한 경우에 kubectl debug
을 통해 명령어를 지정함으로써 해당 파드의
복제본을 인터랙티브 쉘로 생성할 수 있다.
kubectl debug myapp -it --copy-to=myapp-debug --container=myapp -- sh
If you don't see a command prompt, try pressing enter.
/ #
이제 인터랙티브 쉘에 접근할 수 있으니 파일 시스템 경로를 확인하거나 동작 중인 컨테이너의 명령어를 직접 확인하는 등의 작업이 가능하다.
--container
옵션을 통해 해당 컨테이너의
이름을 지정해야만 한다. 이름을 지정하지 않는다면 kubectl debug
은 이전에 지정한 명령어를
그대로 사용해서 컨테이너를 생성할 것이다.-i
플래그는 kubectl debug
명령이 컨테이너에 바로 연결되도록 한다.
이러한 동작을 방지하기 위해서는 --attach=false
옵션을 지정할 수 있다. 만약 여러분이 세션이
종료된다면 kubectl attach
명령을 통해 다시 연결할 수 있다.사용이 모두 끝나면, 디버깅에 사용된 파드들을 잊지 말고 정리한다.
kubectl delete pod myapp myapp-debug
특정한 경우에 여러분은 제대로 동작하지 않는 파드의 이미지를 기존 프로덕션 컨테이너 이미지에서 디버깅 빌드나 추가적인 도구를 포함한 이미지로 변경하고 싶을 수 있다.
이 사례를 보여주기 위해 kubectl run
명령을 통해 파드를 생성하였다.
kubectl run myapp --image=busybox --restart=Never -- sleep 1d
여기서는 kubectl debug
명령을 통해 해당 컨테이너 이미지를 ubuntu
로 변경하며
복제본을 생성하였다.
kubectl debug myapp --copy-to=myapp-debug --set-image=*=ubuntu
--set-image
의 문법은 kubectl set image
와 동일하게 container_name=image
형식의 문법을 사용한다. *=ubuntu
라는 의미는 모든 컨테이너의 이미지를 ubuntu
로
변경하겠다는 의미이다.
사용이 모두 끝나면, 디버깅에 사용된 파드를 잊지 말고 정리한다.
kubectl delete pod myapp myapp-debug
만약 위의 어떠한 방법도 사용할 수 없다면, 파드가 현재 동작 중인 노드를 찾아
해당 노드에서 실행되는 파드를 생성할 수 있다.
다음 kubectl debug
명령을 통해 해당 노드에서 인터랙티브한 쉘을 생성할 수 있다.
kubectl debug node/mynode -it --image=ubuntu
Creating debugging pod node-debugger-mynode-pdx84 with container debugger on node mynode.
If you don't see a command prompt, try pressing enter.
root@ek8s:/#
노드에서 디버깅 세션을 생성할 때 유의해야 할 점은 다음과 같다.
kubectl debug
는 노드의 이름에 기반해 새로운 파드의 이름을
자동으로 생성한다./host
에 마운트된다.chroot /host
등의 작업은 수행될 수 없다.사용이 모두 끝나면, 디버깅에 사용된 파드를 잊지 말고 정리한다.
kubectl delete pod node-debugger-mynode-pdx84
이 페이지는 동작중인 컨테이너에 접근하기 위해 kubectl exec
을 사용하는
방법에 대해 설명한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
이 예시에서는 하나의 컨테이너를 가진 파드를 생성할 것이다. 이 컨테이너는 nginx 이미지를 실행한다. 해당 파드에 대한 설정 파일은 다음과 같다.
apiVersion: v1
kind: Pod
metadata:
name: shell-demo
spec:
volumes:
- name: shared-data
emptyDir: {}
containers:
- name: nginx
image: nginx
volumeMounts:
- name: shared-data
mountPath: /usr/share/nginx/html
hostNetwork: true
dnsPolicy: Default
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/application/shell-demo.yaml
다음을 통해 컨테이너가 동작하고 있는지 확인할 수 있다.
kubectl get pod shell-demo
동작중인 컨테이너의 셸에 접근한다.
kubectl exec --stdin --tty shell-demo -- /bin/bash
--
)를 사용할 수 있다.셸에 접근해서 다음처럼 루트 디렉토리를 확인해 볼 수 있다.
# Run this inside the container
ls /
접근한 셸에서 다른 명령어도 한번 실행해 보아라. 다음은 실행해 볼 명령의 예시이다.
# You can run these example commands inside the container
ls /
cat /proc/mounts
cat /proc/1/maps
apt-get update
apt-get install -y tcpdump
tcpdump
apt-get install -y lsof
lsof
apt-get install -y procps
ps aux
ps aux | grep nginx
앞에서 생성한 파드에 대한 설정을 살펴보아라. 파드에는
emptyDir
볼륨이 사용되었고, 이 컨테이너는 해당 볼륨을
/usr/share/nginx/html
경로에 마운트하였다.
접근한 셸 환경에서 /usr/share/nginx/html
디렉터리에 index.html
파일을
생성해 보아라.
# Run this inside the container
echo 'Hello shell demo' > /usr/share/nginx/html/index.html
셸 환경에서 nginx 서버에 GET 요청을 시도해보면 다음과 같다.
# Run this in the shell inside your container
apt-get update
apt-get install curl
curl http://localhost/
출력 결과는 여러분이 index.html
파일에 작성한 텍스트를 출력할 것이다.
Hello shell demo
셸 사용이 모두 끝났다면 exit
을 입력해 종료하라.
exit # To quit the shell in the container
셸이 아닌 일반적인 커맨드 환경에서 다음처럼 동작중인 컨테이너의 환경 변수를 출력할 수 있다.
kubectl exec shell-demo env
다른 명령어도 한번 실행해 보아라. 다음은 실행해 볼 명령의 예시이다.
kubectl exec shell-demo -- ps aux
kubectl exec shell-demo -- ls /
kubectl exec shell-demo -- cat /proc/1/mounts
만일 파드에 한 개 이상의 컨테이너가 있을 경우, kubectl exec
명령어에
--container
혹은 -c
옵션을 사용해서 컨테이너를 지정하라. 예를 들어,
여러분이 my-pod라는 이름의 파드가 있다고 가정해 보자. 이 파드에는 main-app 과
helper-app 이라는 이름의 두 컨테이너가 있다. 다음 명령어는 main-app
컨테이너에 대한 셸에 접근할 것이다.
kubectl exec -i -t my-pod --container main-app -- /bin/bash
-i
와 -t
는 각각 --stdin
와 --tty
옵션에 대응된다.이 문서는 클러스터 트러블슈팅에 대해 설명한다. 사용자가 겪고 있는 문제의 근본 원인으로서 사용자의 애플리케이션을 이미 배제했다고 가정한다. 애플리케이션 디버깅에 대한 팁은 애플리케이션 트러블슈팅 가이드를 참조한다. 자세한 내용은 트러블슈팅 문서를 참조한다.
클러스터에서 가장 먼저 디버그해야 할 것은 노드가 모두 올바르게 등록되었는지 여부이다.
다음을 실행한다.
kubectl get nodes
그리고 보일 것으로 예상되는 모든 노드가 존재하고 모두 Ready
상태인지 확인한다.
클러스터의 전반적인 상태에 대한 자세한 정보를 얻으려면 다음을 실행할 수 있다.
kubectl cluster-info dump
때때로 디버깅할 때 노드의 상태를 확인하는 것이 유용할 수 있다(예를 들어, 어떤 노드에서 실행되는 파드가 이상하게 행동하는 것을 발견했거나, 특정 노드에 파드가 스케줄링되지 않는 이유를 알아보기 위해). 파드의 경우와 마찬가지로, kubectl describe node
및 kubectl get node -o yaml
명령을 사용하여 노드에 대한 상세 정보를 볼 수 있다. 예를 들어, 노드가 다운 상태(네트워크 연결이 끊어졌거나, kubelet이 종료된 후 재시작되지 못했거나 등)라면 아래와 같은 출력이 나올 것이다. 노드가 NotReady 상태라는 것을 나타내는 이벤트(event)와, 더 이상 실행 중이 아닌 파드(NotReady 상태 이후 5분 뒤에 축출되었음)에 주목한다.
kubectl get nodes
NAME STATUS ROLES AGE VERSION
kube-worker-1 NotReady <none> 1h v1.23.3
kubernetes-node-bols Ready <none> 1h v1.23.3
kubernetes-node-st6x Ready <none> 1h v1.23.3
kubernetes-node-unaj Ready <none> 1h v1.23.3
kubectl describe node kube-worker-1
Name: kube-worker-1
Roles: <none>
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/arch=amd64
kubernetes.io/hostname=kube-worker-1
kubernetes.io/os=linux
Annotations: kubeadm.alpha.kubernetes.io/cri-socket: /run/containerd/containerd.sock
node.alpha.kubernetes.io/ttl: 0
volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp: Thu, 17 Feb 2022 16:46:30 -0500
Taints: node.kubernetes.io/unreachable:NoExecute
node.kubernetes.io/unreachable:NoSchedule
Unschedulable: false
Lease:
HolderIdentity: kube-worker-1
AcquireTime: <unset>
RenewTime: Thu, 17 Feb 2022 17:13:09 -0500
Conditions:
Type Status LastHeartbeatTime LastTransitionTime Reason Message
---- ------ ----------------- ------------------ ------ -------
NetworkUnavailable False Thu, 17 Feb 2022 17:09:13 -0500 Thu, 17 Feb 2022 17:09:13 -0500 WeaveIsUp Weave pod has set this
MemoryPressure Unknown Thu, 17 Feb 2022 17:12:40 -0500 Thu, 17 Feb 2022 17:13:52 -0500 NodeStatusUnknown Kubelet stopped posting node status.
DiskPressure Unknown Thu, 17 Feb 2022 17:12:40 -0500 Thu, 17 Feb 2022 17:13:52 -0500 NodeStatusUnknown Kubelet stopped posting node status.
PIDPressure Unknown Thu, 17 Feb 2022 17:12:40 -0500 Thu, 17 Feb 2022 17:13:52 -0500 NodeStatusUnknown Kubelet stopped posting node status.
Ready Unknown Thu, 17 Feb 2022 17:12:40 -0500 Thu, 17 Feb 2022 17:13:52 -0500 NodeStatusUnknown Kubelet stopped posting node status.
Addresses:
InternalIP: 192.168.0.113
Hostname: kube-worker-1
Capacity:
cpu: 2
ephemeral-storage: 15372232Ki
hugepages-2Mi: 0
memory: 2025188Ki
pods: 110
Allocatable:
cpu: 2
ephemeral-storage: 14167048988
hugepages-2Mi: 0
memory: 1922788Ki
pods: 110
System Info:
Machine ID: 9384e2927f544209b5d7b67474bbf92b
System UUID: aa829ca9-73d7-064d-9019-df07404ad448
Boot ID: 5a295a03-aaca-4340-af20-1327fa5dab5c
Kernel Version: 5.13.0-28-generic
OS Image: Ubuntu 21.10
Operating System: linux
Architecture: amd64
Container Runtime Version: containerd://1.5.9
Kubelet Version: v1.23.3
Kube-Proxy Version: v1.23.3
Non-terminated Pods: (4 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits Age
--------- ---- ------------ ---------- --------------- ------------- ---
default nginx-deployment-67d4bdd6f5-cx2nz 500m (25%) 500m (25%) 128Mi (6%) 128Mi (6%) 23m
default nginx-deployment-67d4bdd6f5-w6kd7 500m (25%) 500m (25%) 128Mi (6%) 128Mi (6%) 23m
kube-system kube-proxy-dnxbz 0 (0%) 0 (0%) 0 (0%) 0 (0%) 28m
kube-system weave-net-gjxxp 100m (5%) 0 (0%) 200Mi (10%) 0 (0%) 28m
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
Resource Requests Limits
-------- -------- ------
cpu 1100m (55%) 1 (50%)
memory 456Mi (24%) 256Mi (13%)
ephemeral-storage 0 (0%) 0 (0%)
hugepages-2Mi 0 (0%) 0 (0%)
Events:
...
kubectl get node kube-worker-1 -o yaml
apiVersion: v1
kind: Node
metadata:
annotations:
kubeadm.alpha.kubernetes.io/cri-socket: /run/containerd/containerd.sock
node.alpha.kubernetes.io/ttl: "0"
volumes.kubernetes.io/controller-managed-attach-detach: "true"
creationTimestamp: "2022-02-17T21:46:30Z"
labels:
beta.kubernetes.io/arch: amd64
beta.kubernetes.io/os: linux
kubernetes.io/arch: amd64
kubernetes.io/hostname: kube-worker-1
kubernetes.io/os: linux
name: kube-worker-1
resourceVersion: "4026"
uid: 98efe7cb-2978-4a0b-842a-1a7bf12c05f8
spec: {}
status:
addresses:
- address: 192.168.0.113
type: InternalIP
- address: kube-worker-1
type: Hostname
allocatable:
cpu: "2"
ephemeral-storage: "14167048988"
hugepages-2Mi: "0"
memory: 1922788Ki
pods: "110"
capacity:
cpu: "2"
ephemeral-storage: 15372232Ki
hugepages-2Mi: "0"
memory: 2025188Ki
pods: "110"
conditions:
- lastHeartbeatTime: "2022-02-17T22:20:32Z"
lastTransitionTime: "2022-02-17T22:20:32Z"
message: Weave pod has set this
reason: WeaveIsUp
status: "False"
type: NetworkUnavailable
- lastHeartbeatTime: "2022-02-17T22:20:15Z"
lastTransitionTime: "2022-02-17T22:13:25Z"
message: kubelet has sufficient memory available
reason: KubeletHasSufficientMemory
status: "False"
type: MemoryPressure
- lastHeartbeatTime: "2022-02-17T22:20:15Z"
lastTransitionTime: "2022-02-17T22:13:25Z"
message: kubelet has no disk pressure
reason: KubeletHasNoDiskPressure
status: "False"
type: DiskPressure
- lastHeartbeatTime: "2022-02-17T22:20:15Z"
lastTransitionTime: "2022-02-17T22:13:25Z"
message: kubelet has sufficient PID available
reason: KubeletHasSufficientPID
status: "False"
type: PIDPressure
- lastHeartbeatTime: "2022-02-17T22:20:15Z"
lastTransitionTime: "2022-02-17T22:15:15Z"
message: kubelet is posting ready status. AppArmor enabled
reason: KubeletReady
status: "True"
type: Ready
daemonEndpoints:
kubeletEndpoint:
Port: 10250
nodeInfo:
architecture: amd64
bootID: 22333234-7a6b-44d4-9ce1-67e31dc7e369
containerRuntimeVersion: containerd://1.5.9
kernelVersion: 5.13.0-28-generic
kubeProxyVersion: v1.23.3
kubeletVersion: v1.23.3
machineID: 9384e2927f544209b5d7b67474bbf92b
operatingSystem: linux
osImage: Ubuntu 21.10
systemUUID: aa829ca9-73d7-064d-9019-df07404ad448
현재로서는 클러스터를 더 깊이 파고들려면 관련 머신에서 로그 확인이 필요하다. 관련 로그 파일
위치는 다음과 같다. (systemd 기반 시스템에서는 journalctl
을 대신 사용해야 할 수도 있다.)
/var/log/kube-apiserver.log
- API 서버, API 제공을 담당/var/log/kube-scheduler.log
- 스케줄러, 스케줄 결정을 담당/var/log/kube-controller-manager.log
- 레플리케이션 컨트롤러를 담당하는 컨트롤러/var/log/kubelet.log
- Kubelet, 노드에서 컨테이너 실행을 담당/var/log/kube-proxy.log
- Kube Proxy, 서비스 로드밸런싱을 담당아래에 일부 오류 상황 예시 및 문제를 완화하기 위해 클러스터 설정을 조정하는 방법을 나열한다.
조치: IaaS VM을 위한 IaaS 공급자의 자동 VM 다시 시작 기능을 사용한다.
조치: API 서버+etcd가 있는 VM에 IaaS 제공자의 안정적인 스토리지(예: GCE PD 또는 AWS EBS 볼륨)를 사용한다.
조치: 고가용성 구성을 사용한다.
조치: API 서버 PD/EBS 볼륨의 주기적인 스냅샷
조치: 파드 앞에 레플리케이션 컨트롤러와 서비스 사용
조치: 예기치 않은 재시작을 허용하도록 설계된 애플리케이션(컨테이너)
crictl
을 사용하여 쿠버네티스 노드를 디버깅한다.telepresence
를 사용하여 서비스를 로컬에서 개발 및 디버깅한다.쿠버네티스에서, 메트릭 API(Metrics API) 는 자동 스케일링 및 비슷한 사용 사례를 지원하기 위한 기본적인 메트릭 집합을 제공한다. 이 API는 노드와 파드의 리소스 사용량 정보를 제공하며, 여기에는 CPU 및 메모리 메트릭이 포함된다. 메트릭 API를 클러스터에 배포하면, 쿠버네티스 API의 클라이언트는 이 정보에 대해 질의할 수 있으며, 질의 권한을 관리하기 위해 쿠버네티스의 접근 제어 메커니즘을 이용할 수 있다.
HorizontalPodAutoscaler(HPA) 및 VerticalPodAutoscaler(VPA)는 사용자의 요구 사항을 만족할 수 있도록 워크로드 레플리카와 리소스를 조정하는 데에 메트릭 API의 데이터를 이용한다.
kubectl top
명령을 이용하여
리소스 메트릭을 볼 수도 있다.
그림 1은 리소스 메트릭 파이프라인의 아키텍처를 나타낸다.
그림 1. 리소스 메트릭 파이프라인
그림의 오른쪽에서 왼쪽 순으로, 아키텍처 구성 요소는 다음과 같다.
cAdvisor: kubelet에 포함된 컨테이너 메트릭을 수집, 집계, 노출하는 데몬
kubelet: 컨테이너 리소스 관리를 위한 노드 에이전트.
리소스 메트릭은 kubelet API 엔드포인트 /metrics/resource
및
/stats
를 사용하여 접근 가능하다.
요약 API: /stats
엔드포인트를 통해 사용할 수 있는
노드 별 요약된 정보를 탐색 및 수집할 수 있도록 kubelet이 제공하는 API
metrics-server: 각 kubelet으로부터 수집한 리소스 메트릭을 수집 및 집계하는 클러스터 애드온 구성 요소.
API 서버는 HPA, VPA 및 kubectl top
명령어가 사용할 수 있도록 메트릭 API를 제공한다.
metrics-server는 메트릭 API에 대한 기준 구현(reference implementation) 중 하나이다.
메트릭 API: 워크로드 오토스케일링에 사용되는 CPU 및 메모리 정보로의 접근을 지원하는 쿠버네티스 API. 이를 클러스터에서 사용하려면, 메트릭 API를 제공하는 API 확장(extension) 서버가 필요하다.
Kubernetes 1.8 [beta]
metrics-server는 메트릭 API에 대한 구현이다. 이 API는 클러스터 내 노드와 파드의 CPU 및 메모리 사용 정보에 접근할 수 있게 해 준다. 이것의 주 역할은 리소스 사용 메트릭을 쿠버네티스 오토스케일러 구성 요소에 제공하는 것이다.
다음은 minikube
노드에 대한 메트릭 API 요청 예시이며
가독성 향상을 위해 jq
를 활용한다.
kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes/minikube" | jq '.'
다음은 curl
을 이용하여 동일한 API 호출을 하는 명령어다.
curl http://localhost:8080/apis/metrics.k8s.io/v1beta1/nodes/minikube
응답 예시는 다음과 같다.
{
"kind": "NodeMetrics",
"apiVersion": "metrics.k8s.io/v1beta1",
"metadata": {
"name": "minikube",
"selfLink": "/apis/metrics.k8s.io/v1beta1/nodes/minikube",
"creationTimestamp": "2022-01-27T18:48:43Z"
},
"timestamp": "2022-01-27T18:48:33Z",
"window": "30s",
"usage": {
"cpu": "487558164n",
"memory": "732212Ki"
}
}
다음은 kube-system
네임스페이스 내의 kube-scheduler-minikube
파드에 대한
메트릭 API 요청 예시이며 가독성 향상을 위해 jq
를 활용한다.
kubectl get --raw "/apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/kube-scheduler-minikube" | jq '.'
다음은 curl
을 이용하여 동일한 API 호출을 하는 명령어다.
curl http://localhost:8080/apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/kube-scheduler-minikube
응답 예시는 다음과 같다.
{
"kind": "PodMetrics",
"apiVersion": "metrics.k8s.io/v1beta1",
"metadata": {
"name": "kube-scheduler-minikube",
"namespace": "kube-system",
"selfLink": "/apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/kube-scheduler-minikube",
"creationTimestamp": "2022-01-27T19:25:00Z"
},
"timestamp": "2022-01-27T19:24:31Z",
"window": "30s",
"containers": [
{
"name": "kube-scheduler",
"usage": {
"cpu": "9559630n",
"memory": "22244Ki"
}
}
]
}
메트릭 API는 k8s.io/metrics 저장소에 정의되어 있다.
metrics.k8s.io
API를 사용하기 위해서는
API 집계(aggregation) 계층을 활성화하고
APIService를 등록해야 한다.
메트릭 API에 대해 더 알아보려면, 리소스 메트릭 API 디자인, metrics-server 저장소 및 리소스 메트릭 API를 참고한다.
CPU는 cpu
단위로 측정된 평균 코어 사용량 형태로 보고된다. 쿠버네티스에서 1 cpu는
클라우드 제공자의 경우 1 vCPU/코어에 해당하고, 베어메탈 인텔 프로세서의 경우 1 하이퍼-스레드에 해당한다.
이 값은 커널(Linux 및 Windows 커널 모두)에서 제공하는 누적 CPU 카운터에 대한
비율을 취하여 얻어진다.
CPU 값 계산에 사용된 타임 윈도우는 메트릭 API의 window
필드에 표시된다.
쿠버네티스가 어떻게 CPU 리소스를 할당하고 측정하는지 더 알아보려면, CPU의 의미를 참고한다.
메모리는 메트릭을 수집하는 순간에 바이트 단위로 측정된 워킹 셋(working set) 형태로 보고된다.
이상적인 환경에서, "워킹 셋"은 메모리가 부족한 상태더라도 해제할 수 없는 사용 중인 메모리의 양이다. 그러나 워킹 셋의 계산 방법은 호스트 OS에 따라 다르며 일반적으로 추정치를 추출하기 위해 휴리스틱을 많이 사용한다.
컨테이너의 워킹 셋에 대한 쿠버네티스 모델은 컨테이너 런타임이 해당 컨테이너와 연결된 익명(anonymous) 메모리를 계산할 것으로 예상한다. 호스트 OS가 항상 페이지를 회수할 수는 없기 때문에, 워킹 셋 메트릭에는 일반적으로 일부 캐시된 (파일 기반) 메모리도 포함된다.
쿠버네티스가 어떻게 메모리 리소스를 할당하고 측정하는지 더 알아보려면, 메모리의 의미를 참고한다.
metrics-server는 kubelet으로부터 리소스 메트릭을 수집하고,
이를 HPA(Horizontal Pod Autoscaler) 및 VPA(Vertical Pod Autoscaler)가 활용할 수 있도록 쿠버네티스 API 서버 내에서 메트릭 API(Metrics API)를 통해 노출한다.
kubectl top
명령을 사용하여 이 메트릭을 확인해볼 수도 있다.
metrics-server는 쿠버네티스 API를 사용하여 클러스터의 노드와 파드를 추적한다. metrics-server는 각 노드에 HTTP를 통해 질의하여 메트릭을 수집한다. metrics-server는 또한 파드 메타데이터의 내부적 뷰를 작성하고, 파드 헬스(health)에 대한 캐시를 유지한다. 이렇게 캐시된 파드 헬스 정보는 metrics-server가 제공하는 확장 API(extension API)를 통해 이용할 수 있다.
HPA 질의에 대한 예시에서, 예를 들어 HPA 질의에 대한 경우, metrics-server는 디플로이먼트의 어떤 파드가 레이블 셀렉터 조건을 만족하는지 판별해야 한다.
metrics-server는 각 노드로부터 메트릭을 수집하기 위해 kubelet API를 호출한다. 사용 중인 metrics-server 버전에 따라, 다음의 엔드포인트를 사용한다.
/metrics/resource
/stats/summary
metrics-server에 대한 더 많은 정보는 metrics-server 저장소를 확인한다.
또한 다음을 참고할 수도 있다.
kubelet이 어떻게 노드 메트릭을 제공하는지, 그리고 쿠버네티스 API를 통해 이러한 메트릭에 어떻게 접근하는지 알아보려면, 노드 메트릭 데이터 문서를 참조한다.
애플리케이션을 스케일하여 신뢰할 수 있는 서비스를 제공하려면, 애플리케이션이 배포되었을 때 애플리케이션이 어떻게 동작하는지를 이해해야 한다. 컨테이너, 파드, 서비스, 그리고 전체 클러스터의 특성을 검사하여 쿠버네티스 클러스터 내의 애플리케이션 성능을 검사할 수 있다. 쿠버네티스는 각 레벨에서 애플리케이션의 리소스 사용량에 대한 상세 정보를 제공한다. 이 정보는 애플리케이션의 성능을 평가하고 병목 현상을 제거하여 전체 성능을 향상할 수 있게 해준다.
쿠버네티스에서 애플리케이션 모니터링은 단일 모니터링 솔루션에 의존하지 않는다. 신규 클러스터에서는, 리소스 메트릭 또는 완전한 메트릭 파이프라인으로 모니터링 통계를 수집할 수 있다.
리소스 메트릭 파이프라인은
Horizontal Pod Autoscaler
컨트롤러와 같은 클러스터 구성요소나
kubectl top
유틸리티에 관련되어 있는
메트릭들로 제한된 집합을 제공한다. 이 메트릭은 경량의 단기 인메모리 저장소인
metrics-server에
의해서 수집되며 metrics.k8s.io
API를 통해 노출된다.
metrics-server는 클러스터 상의 모든 노드를 발견하고
각 노드의 kubelet에
CPU와 메모리 사용량을 질의한다.
Kubelet은 쿠버네티스 마스터와 노드 간의 다리 역할을 하면서
머신에서 구동되는 파드와 컨테이너를 관리한다.
Kubelet은 각각의 파드를 해당하는 컨테이너에 매치시키고
컨테이너 런타임 인터페이스를 통해
컨테이너 런타임에서 개별 컨테이너의 사용량 통계를 가져온다.
컨테이너를 구현하기 위해 리눅스 cgroup 및 네임스페이스를 활용하는 컨테이너 런타임을 사용하며,
해당 컨테이너 런타임이 사용 통계치를 퍼블리싱 하지 않는 경우,
kubelet은 해당 통계치를 (cAdvisor의 코드 사용하여) 직접 조회 할 수 있다.
이런 통계가 어떻게 도착하든 kubelet은 취합된 파드 리소스 사용량 통계를
metric-server 리소스 메트릭 API를 통해 노출한다.
이 API는 kubelet의 인증이 필요한 읽기 전용 포트 상의
/metrics/resource/v1beta1
에서 제공된다.
완전한 메트릭 파이프라인은 보다 풍부한 메트릭에 접근할 수 있도록 해준다.
쿠버네티스는 Horizontal Pod Autoscaler와 같은 메커니즘을 활용해서 이런 메트릭에
대한 반응으로 클러스터의 현재 상태를 기반으로 자동으로 스케일링하거나 클러스터를
조정할 수 있다. 모니터링 파이프라인은 kubelet에서 메트릭을 가져와서 쿠버네티스에
custom.metrics.k8s.io
와 external.metrics.k8s.io
API를 구현한 어댑터를 통해
노출한다.
CNCF 프로젝트인 프로메테우스는 기본적으로 쿠버네티스, 노드, 프로메테우스 자체를 모니터링할 수 있다. CNCF 프로젝트가 아닌 완전한 메트릭 파이프라인 프로젝트는 쿠버네티스 문서의 범위가 아니다.
다음과 같은 추가 디버깅 도구에 대해 더 알아본다.
노드 문제 감지기(Node Problem Detector) 는 노드의 헬스에 대해 모니터링 및 보고하는 데몬이다.
노드 문제 감지기를 데몬셋(DaemonSet)
혹은 스탠드얼론 데몬(standalone daemon)으로 실행할 수 있다.
노드 문제 감지기는 다양한 데몬으로부터 노드의 문제에 관한 정보를 다양한 데몬으로부터 수집하고,
이러한 컨디션들을 노드컨디션(NodeCondition) 및
이벤트(Event)형태로 API 서버에 보고한다.
노드 문제 감지기 설치 및 사용 방법을 보려면, 노드 문제 감지기 프로젝트 문서를 참조하자.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
노드 문제 감지기는 파일 기반의 커널 로그만 지원한다.
journald
와 같은 로그 도구는 지원하지 않는다.
노드 문제 감지기는 커널 로그 형식을 사용하여 커널 이슈를 보고한다. 커널 로그 형식을 확장하는 방법을 배우려면 기타 로그 형식 지원 추가를 살펴보자.
일부 클라우드 사업자는 노드 문제 감지기를 애드온 으로서 제공한다.
또한, kubectl
을 이용하거나 애드온 파드를 생성하여 노드 문제 감지기를 활성화할 수도 있다.
kubectl
은 노드 문제 감지기를 관리하는 가장 유연한 방법이다.
현재 환경에 맞게 조정하거나 사용자 정의 노드 문제를 탐지하기 위해
기본 설정값을 덮어쓸 수 있다. 예를 들면 아래와 같다.
node-problem-detector.yaml
와 유사하게 노드 문제 감지기 구성을 생성한다:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-problem-detector-v0.1
namespace: kube-system
labels:
k8s-app: node-problem-detector
version: v0.1
kubernetes.io/cluster-service: "true"
spec:
selector:
matchLabels:
k8s-app: node-problem-detector
version: v0.1
kubernetes.io/cluster-service: "true"
template:
metadata:
labels:
k8s-app: node-problem-detector
version: v0.1
kubernetes.io/cluster-service: "true"
spec:
hostNetwork: true
containers:
- name: node-problem-detector
image: registry.k8s.io/node-problem-detector:v0.1
securityContext:
privileged: true
resources:
limits:
cpu: "200m"
memory: "100Mi"
requests:
cpu: "20m"
memory: "20Mi"
volumeMounts:
- name: log
mountPath: /log
readOnly: true
volumes:
- name: log
hostPath:
path: /var/log/
kubectl
을 이용하여 노드 문제 감지기를 시작한다.
kubectl apply -f https://k8s.io/examples/debug/node-problem-detector.yaml
만약 커스텀 클러스터 부트스트랩 솔루션을 사용중이고 기본 설정값을 덮어쓸 필요가 없다면, 디플로이먼트를 추가로 자동화하기 위해 애드온 파드를 활용할 수 있다.
node-problem-detector.yaml
를 생성하고,
컨트롤 플레인 노드의 애드온 파드의 디렉토리 /etc/kubernetes/addons/node-problem-detector
에 설정을 저장한다.
노드 문제 감지기를 빌드할 때, 기본 설정이 포함되어 있다.
하지만 컨피그맵(ConfigMap)
을 이용해
설정을 덮어쓸 수 있다.
config/
내의 설정 파일을 변경한다.
node-problem-detector-config
컨피그맵(ConfigMap)
을 생성한다.
kubectl create configmap node-problem-detector-config --from-file=config/
컨피그맵(ConfigMap)
을 사용하도록 node-problem-detector.yaml
을 변경한다.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-problem-detector-v0.1
namespace: kube-system
labels:
k8s-app: node-problem-detector
version: v0.1
kubernetes.io/cluster-service: "true"
spec:
selector:
matchLabels:
k8s-app: node-problem-detector
version: v0.1
kubernetes.io/cluster-service: "true"
template:
metadata:
labels:
k8s-app: node-problem-detector
version: v0.1
kubernetes.io/cluster-service: "true"
spec:
hostNetwork: true
containers:
- name: node-problem-detector
image: registry.k8s.io/node-problem-detector:v0.1
securityContext:
privileged: true
resources:
limits:
cpu: "200m"
memory: "100Mi"
requests:
cpu: "20m"
memory: "20Mi"
volumeMounts:
- name: log
mountPath: /log
readOnly: true
- name: config # config/ 디렉토리를 컨피그맵 볼륨(ConfigMap volume)으로 덮어쓴다
mountPath: /config
readOnly: true
volumes:
- name: log
hostPath:
path: /var/log/
- name: config # 컨피그맵 볼륨(ConfigMap volume)을 정의한다
configMap:
name: node-problem-detector-config
새로운 설정 파일을 사용하여 노드 문제 감지기를 재생성한다.
# 만약 노드 문제 감지기가 동작하고 있다면, 재생성 전 삭제한다
kubectl delete -f https://k8s.io/examples/debug/node-problem-detector.yaml
kubectl apply -f https://k8s.io/examples/debug/node-problem-detector-configmap.yaml
kubectl
로 시작했을 때에만 적용된다.만약 노드 문제 감지기가 클러스터 애드온으로 실행된 경우, 설정 덮어쓰기가 지원되지 않는다.
애드온 매니저는 컨피그맵(ConfigMap)
을 지원하지 않는다.
커널 모니터는 노드 문제 감지기에서 지원하는 시스템 로그 모니터링 데몬이다. 커널 모니터는 커널 로그를 감시하며, 미리 설정된 규칙에 따라 알려진 커널 이슈를 감지한다.
커널 모니터는 config/kernel-monitor.json
에
미리 설정된 규칙 모음과 커널 이슈를 매칭한다.
규칙 리스트는 확장 가능하다. 설정을 덮어쓰기 해 규칙 리스트를 확장할 수 있다.
신규 NodeCondition
를 지원하려면, config/kernel-monitor.json
의 conditions
필드 내 조건 정의를 생성해야한다.
예를 들면 아래와 같다.
{
"type": "NodeConditionType",
"reason": "CamelCaseDefaultNodeConditionReason",
"message": "arbitrary default node condition message"
}
신규 문제를 감지하려면 config/kernel-monitor.json
의 rules
필드를
신규 규칙 정의로 확장하면 된다.
{
"type": "temporary/permanent",
"condition": "NodeConditionOfPermanentIssue",
"reason": "CamelCaseShortReason",
"message": "regexp matching the issue in the kernel log"
}
운영 체제 (OS) 배포판의 커널 로그 경로를 확인한다.
리눅스 커널 로그 장치(log device)는 보통 /dev/kmsg
와 같이 표시된다. 하지만, 로그 경로 장소는 OS 배포판마다 상이하다.
config/kernel-monitor.json
의 log
필드는 컨테이너 내부의 로그 경로를 나타낸다.
log
필드를 노드 문제 감지기가 감시하는 장치 경로와 일치하도록 구성하면 된다.
커널 모니터는 커널 로그의 내부 데이터 구조를 해석하기 위해
Translator
플러그인을 사용한다.
신규 로그 포맷을 사용하기 위해 신규 해석기를 구현할 수 있다.
노드 헬스를 모니터링하기 위해 클러스터에 노드 문제 탐지기를 실행할 것을 권장한다. 노드 문제 감지기를 실행할 때, 각 노드에 추가 리소스 오버헤드가 발생할 수 있다. 다음과 같은 이유 때문에 일반적으로는 문제가 없다.
Kubernetes v1.11 [stable]
crictl
은 CRI-호환 컨테이너 런타임에 사용할 수 있는 커맨드라인 인터페이스이다.
쿠버네티스 노드에서 컨테이너 런타임과 애플리케이션을 검사하고
디버그하는 데 사용할 수 있다.
crictl
과 그 소스는 cri-tools 저장소에서 호스팅한다.
crictl
은 CRI 런타임이 있는 리눅스 운영체제를 필요로 한다.
cri-tools 릴리스 페이지에서
다양한 아키텍처 별로 압축된 crictl
아카이브(archive)를 다운로드할 수 있다.
설치된 쿠버네티스 버전에 해당하는 버전을 다운로드한다.
/usr/local/bin/
와 같은 시스템 경로의 위치에
압축을 푼다.
crictl
커맨드에는 여러 하위 커맨드와 런타임 플래그가 있다.
자세한 내용은 crictl help
또는 crictl <subcommand> help
를 참조한다.
아래 내용 중 하나를 통해 crictl
의 엔드포인트를 설정할 수 있다.
--runtime-endpoint
와 --image-endpoint
플래그 설정.CONTAINER_RUNTIME_ENDPOINT
와 IMAGE_SERVICE_ENDPOINT
환경 변수
설정./etc/crictl.yaml
에 엔드포인트 설정.
다른 파일을 지정하기 위해서는 crictl
을 실행할 때 --config=PATH_TO_FILE
플래그를 사용한다.crictl
이 알려진 엔드포인트 목록에 연결을 시도하므로 성능에 영향을 줄 수 있다.서버에 연결할 때 구성 파일에서 timeout
또는 debug
값을 명시하거나,
--timeout
그리고 --debug
커맨드라인 플래그를 사용하여
타임아웃 값을 지정하고 디버깅을 활성화하거나 비활성화할 수 있다.
현재 구성을 보거나 편집하려면 /etc/crictl.yaml
의 내용을 보거나 편집한다.
예를 들어,
containerd
컨테이너 런타임 사용 시 구성은 아래와 유사하다.
runtime-endpoint: unix:///var/run/containerd/containerd.sock
image-endpoint: unix:///var/run/containerd/containerd.sock
timeout: 10
debug: true
crictl
에 대해 자세히 알고 싶다면,
crictl
문서를 참조한다.
아래 예시를 통해 crictl
커맨드와 출력을 확인해보자.
crictl
을 사용하여 파드 샌드박스(sandbox)나 컨테이너를 만들게 되면,
결국에는 kubelet이 그것들을 삭제하게 된다.
crictl
은 일반적인 워크플로우 툴이 아니라 디버깅에 유용한 툴임을 명심해야 한다.모든 파드의 목록 조회
crictl pods
출력은 다음과 유사하다.
POD ID CREATED STATE NAME NAMESPACE ATTEMPT
926f1b5a1d33a About a minute ago Ready sh-84d7dcf559-4r2gq default 0
4dccb216c4adb About a minute ago Ready nginx-65899c769f-wv2gp default 0
a86316e96fa89 17 hours ago Ready kube-proxy-gblk4 kube-system 0
919630b8f81f1 17 hours ago Ready nvidia-device-plugin-zgbbv kube-system 0
이름으로 파드의 목록 조회
crictl pods --name nginx-65899c769f-wv2gp
출력은 다음과 유사하다.
POD ID CREATED STATE NAME NAMESPACE ATTEMPT
4dccb216c4adb 2 minutes ago Ready nginx-65899c769f-wv2gp default 0
레이블로 파드의 목록 조회
crictl pods --label run=nginx
출력은 다음과 유사하다.
POD ID CREATED STATE NAME NAMESPACE ATTEMPT
4dccb216c4adb 2 minutes ago Ready nginx-65899c769f-wv2gp default 0
모든 이미지의 목록 조회
crictl images
출력은 다음과 유사하다.
IMAGE TAG IMAGE ID SIZE
busybox latest 8c811b4aec35f 1.15MB
k8s-gcrio.azureedge.net/hyperkube-amd64 v1.10.3 e179bbfe5d238 665MB
k8s-gcrio.azureedge.net/pause-amd64 3.1 da86e6ba6ca19 742kB
nginx latest cd5239a0906a6 109MB
저장소로 이미지 목록 조회
crictl images nginx
출력은 다음과 유사하다.
IMAGE TAG IMAGE ID SIZE
nginx latest cd5239a0906a6 109MB
이미지의 IDs 목록만 조회
crictl images -q
출력은 다음과 유사하다.
sha256:8c811b4aec35f259572d0f79207bc0678df4c736eeec50bc9fec37ed936a472a
sha256:e179bbfe5d238de6069f3b03fccbecc3fb4f2019af741bfff1233c4d7b2970c5
sha256:da86e6ba6ca197bf6bc5e9d900febd906b133eaa4750e6bed647b0fbe50ed43e
sha256:cd5239a0906a6ccf0562354852fae04bc5b52d72a2aff9a871ddb6bd57553569
모든 컨테이너 목록 조회
crictl ps -a
출력은 다음과 유사하다.
CONTAINER ID IMAGE CREATED STATE NAME ATTEMPT
1f73f2d81bf98 busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47 7 minutes ago Running sh 1
9c5951df22c78 busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47 8 minutes ago Exited sh 0
87d3992f84f74 nginx@sha256:d0a8828cccb73397acb0073bf34f4d7d8aa315263f1e7806bf8c55d8ac139d5f 8 minutes ago Running nginx 0
1941fb4da154f k8s-gcrio.azureedge.net/hyperkube-amd64@sha256:00d814b1f7763f4ab5be80c58e98140dfc69df107f253d7fdd714b30a714260a 18 hours ago Running kube-proxy 0
실행 중인 컨테이너 목록 조회
crictl ps
출력은 다음과 유사하다.
CONTAINER ID IMAGE CREATED STATE NAME ATTEMPT
1f73f2d81bf98 busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47 6 minutes ago Running sh 1
87d3992f84f74 nginx@sha256:d0a8828cccb73397acb0073bf34f4d7d8aa315263f1e7806bf8c55d8ac139d5f 7 minutes ago Running nginx 0
1941fb4da154f k8s-gcrio.azureedge.net/hyperkube-amd64@sha256:00d814b1f7763f4ab5be80c58e98140dfc69df107f253d7fdd714b30a714260a 17 hours ago Running kube-proxy 0
crictl exec -i -t 1f73f2d81bf98 ls
출력은 다음과 유사하다.
bin dev etc home proc root sys tmp usr var
모든 컨테이너의 로그 조회
crictl logs 87d3992f84f74
출력은 다음과 유사하다.
10.240.0.96 - - [06/Jun/2018:02:45:49 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.96 - - [06/Jun/2018:02:45:50 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.96 - - [06/Jun/2018:02:45:51 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
최신 N
개 줄의 로그만 조회
crictl logs --tail=1 87d3992f84f74
출력은 다음과 유사하다.
10.240.0.96 - - [06/Jun/2018:02:45:51 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
crictl
을 사용하여 파드 샌드박스를 실행하는 것은 컨테이너 런타임 디버깅에 유용하다.
구동 중인 쿠버네티스 클러스터에서
이러한 샌드박스는 kubelet에 의해서 결국 중지 및 삭제된다.
다음과 같은 JSON 파일 생성한다.
{
"metadata": {
"name": "nginx-sandbox",
"namespace": "default",
"attempt": 1,
"uid": "hdishd83djaidwnduwk28bcsb"
},
"log_directory": "/tmp",
"linux": {
}
}
crictl runp
커맨드를 사용하여 JSON을 적용하고 샌드박스를 실행한다.
crictl runp pod-config.json
결과로 샌드박스의 ID가 반환될 것이다.
crictl
을 사용하여 컨테이너를 만드는 것은 컨테이너 런타임 디버깅에 유용하다.
구동 중인 쿠버네티스 클러스터에서
이러한 샌드박스는 kubelet에 의해서 결국 중지 및 삭제된다.
busybox 이미지 가져오기
crictl pull busybox
Image is up to date for busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
파드와 컨테이너 구성(config) 생성
파드 구성:
{
"metadata": {
"name": "busybox-sandbox",
"namespace": "default",
"attempt": 1,
"uid": "aewi4aeThua7ooShohbo1phoj"
},
"log_directory": "/tmp",
"linux": {
}
}
컨테이너 구성:
{
"metadata": {
"name": "busybox"
},
"image":{
"image": "busybox"
},
"command": [
"top"
],
"log_path":"busybox.log",
"linux": {
}
}
이전에 생성한 파드의 ID 및 컨테이너 구성 파일과 파드 구성 파일을 커맨드 인자로 전달하여, 컨테이너를 생성한다. 결과로 컨테이너의 ID가 반환될 것이다.
crictl create f84dd361f8dc51518ed291fbadd6db537b0496536c1d2d6c05ff943ce8c9a54f container-config.json pod-config.json
모든 컨테이너의 목록을 조회하여
새로 생성된 컨테이너의 상태가 Created
임을 확인한다.
crictl ps -a
출력은 다음과 유사하다.
CONTAINER ID IMAGE CREATED STATE NAME ATTEMPT
3e025dd50a72d busybox 32 seconds ago Created busybox 0
컨테이너를 시작하기 위해서 컨테이너 ID를 crictl start
에 인자로 전달한다.
crictl start 3e025dd50a72d956c4f14881fbb5b1080c9275674e95fb67f965f6478a957d60
출력은 다음과 유사하다.
3e025dd50a72d956c4f14881fbb5b1080c9275674e95fb67f965f6478a957d60
컨테이너의 상태가 Running
임을 확인한다.
crictl ps
출력은 다음과 유사하다.
CONTAINER ID IMAGE CREATED STATE NAME ATTEMPT
3e025dd50a72d busybox About a minute ago Running busybox 0
쿠버네티스 감사(auditing) 는 클러스터의 작업 순서를 문서화하는 보안 관련 시간별 레코드 세트를 제공한다. 클러스터는 사용자, 쿠버네티스 API를 사용하는 애플리케이션 및 컨트롤 플레인 자체에서 생성된 활동을 감사한다.
감사를 통해 클러스터 관리자는 다음 질문에 답할 수 있다.
감사 기록은 kube-apiserver 컴포넌트 내에서 수명주기를 시작한다. 실행의 각 단계에서 각 요청은 감사 이벤트를 생성하고, 감사 이벤트는 특정 정책에 따라 사전 처리되고 백엔드에 기록된다. 정책은 기록된 내용을 결정하고 백엔드는 기록을 유지한다. 현재 백엔드 구현에는 로그 파일 및 웹훅이 포함된다.
각 요청들은 연관된 단계(stage) 와 함께 기록될 수 있다. 정의된 단계는 다음과 같다.
RequestReceived
- 감사 핸들러가 요청을 수신한 직후,
그리고 핸들러 체인으로 위임되기 전에
생성되는 이벤트에 대한 단계이다.ResponseStarted
- 응답 헤더는 전송되었지만,
응답 본문(body)은 전송되기 전인 단계이다.
이 단계는 오래 실행되는 요청(예: watch)에 대해서만 생성된다.ResponseComplete
- 응답 내용이 완료되었으며,
더 이상 바이트가 전송되지 않을 때의 단계이다.Panic
- 패닉이 발생했을 때 생성되는 이벤트이다.감사 로깅 기능은 감사에 필요한 일부 컨텍스트가 요청마다 저장되기 때문에 API 서버의 메모리 사용량을 증가시킨다. 메모리 소비량은 감사 로깅 구성에 따라 다르다.
감사 정책은 기록해야 하는 이벤트와 포함해야 하는
데이터에 대한 규칙을 정의한다. 감사 정책 오브젝트 구조는
audit.k8s.io
API 그룹에 정의되어 있다.
이벤트가 처리되면 규칙 목록과 순서대로 비교된다.
첫번째 일치 규칙은 이벤트의 감사 수준(audit level) 을 설정한다.
정의된 감사 수준은 다음과 같다.
None
- 이 규칙에 해당되는 이벤트는 로깅하지 않는다.Metadata
- 요청 메타데이터(요청하는 사용자, 타임스탬프, 리소스, 동사(verb) 등)는 로깅하지만
요청/응답 본문은 로깅하지 않는다.Request
- 이벤트 메타데이터 및 요청 본문을 로깅하지만 응답 본문은 로깅하지 않는다.
리소스 외의 요청에는 적용되지 않는다.RequestResponse
- 이벤트 메타데이터 및 요청/응답 본문을 로깅한다.
리소스 외의 요청에는 적용되지 않는다.--audit-policy-file
플래그를 사용하여 정책이 포함된 파일을
kube-apiserver
에 전달할 수 있다. 플래그를 생략하면 이벤트가 기록되지 않는다.
감사 정책 파일에 rules
필드 반드시 가 제공되어야 한다.
규칙이 없는(0개인) 정책은 적절하지 않은(illegal) 것으로 간주된다.
다음은 감사 정책 파일의 예이다.
apiVersion: audit.k8s.io/v1 # 필수사항임.
kind: Policy
# Request Received 단계의 모든 요청에 대해 감사 이벤트를 생성하지 않음.
omitStages:
- "RequestReceived"
rules:
# RequestResponse 수준에서 파드 변경 사항 기록
- level: RequestResponse
resources:
- group: ""
# 리소스 "파드" 가 RBAC 정책과 부합하는 파드의 하위 리소스에 대한
# 요청과 일치하지 않음.
resources: ["pods"]
# 메타데이터 수준에서 "pods/log", "pods/status"를 기록함.
- level: Metadata
resources:
- group: ""
resources: ["pods/log", "pods/status"]
# "controller-leader" 라는 컨피그맵에 요청을 기록하지 않음."
- level: None
resources:
- group: ""
resources: ["configmaps"]
resourceNames: ["controller-leader"]
# 엔드포인트 또는 서비스의 "system:kube-proxy"에 의한 감시 요청 기록하지 않음.
- level: None
users: ["system:kube-proxy"]
verbs: ["watch"]
resources:
- group: "" # 핵심 API 그룹
resources: ["endpoints", "services"]
# 인증된 요청을 특정 리소스가 아닌 URL 경로에 기록하지 않음.
- level: None
userGroups: ["system:authenticated"]
nonResourceURLs:
- "/api*" # 와일드카드 매칭(wildcard matching).
- "/version"
# kube-system에 컨피그맵 변경 사항의 요청 본문을 기록함.
- level: Request
resources:
- group: "" # 핵심 API 그룹
resources: ["configmaps"]
# 이 정책은 "kube-system" 네임스페이스의 리소스에만 적용됨.
# 빈 문자열 "" 은 네임스페이스가 없는 리소스를 선택하는데 사용할 수 있음.
namespaces: ["kube-system"]
# 메타데이터 수준에서 다른 모든 네임스페이스의 컨피그맵과 시크릿 변경 사항을 기록함.
- level: Metadata
resources:
- group: "" # 핵심 API 그룹
resources: ["secrets", "configmaps"]
# 요청 수준에서 코어 및 확장에 있는 다른 모든 리소스를 기록함.
- level: Request
resources:
- group: "" # 핵심 API 그룹
- group: "extensions" # 그룹의 버전을 기재하면 안 된다.
# 메타데이터 수준에서 다른 모든 요청을 기록하기 위한 모든 수집 정책.
- level: Metadata
# 이 정책에 해당하는 감시자와 같은 장기 실행 요청은
# RequestReceived에서 감사 이벤트를 생성하지 않음.
omitStages:
- "RequestReceived"
최소 감사 정책 파일을 사용하여 Metadata
수준에서 모든 요청을 기록할 수 있다.
# Log all requests at the Metadata level.
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
자체 감사 프로필을 만드는 경우 Google Container-Optimized OS에 대한 감사 프로필을 시작점으로 사용할 수 있다. 감사 정책 파일을 생성하는 configure-helper.sh 스크립트를 확인하면 된다. 스크립트로 대부분의 감사 정책 파일을 볼 수 있다.
정의된 필드에 대한 자세한 내용은 Policy
configuration reference를
참조할 수도 있다.
감사 백엔드는 감사 이벤트를 외부 저장소에 유지한다. 기본적으로 kube-apiserver는 두 가지 백엔드를 제공한다.
모든 경우에 감사 이벤트는 쿠버네티스 API의
audit.k8s.io
API 그룹에서 정의한 구조를 따른다.
패치의 경우 요청 내용은 적절한 쿠버네티스 API 오브젝트를 포함하는 JSON 오브젝트가 아니라
패치 작업을 포함하는 JSON 배열이다. 예를 들어 다음 요청 내용은
/apis/batch/v1/namespaces/some-namespace/jobs/some-job-name
에 대한 유효한 패치 요청이다.
[
{
"op": "replace",
"path": "/spec/parallelism",
"value": 0
},
{
"op": "remove",
"path": "/spec/template/spec/containers/0/terminationMessagePolicy"
}
]
로그 백엔드는 감사이벤트를 JSONlines 형식으로 파일에 기록한다.
다음의 kube-apiserver
플래그를 사용하여 로그 감사 백엔드를 구성할 수 있다.
--audit-log-path
는 로그 백엔드가 감사 이벤트를 쓰는 데 사용하는 로그 파일 경로를 지정한다.
이 플래그를 지정하지 않으면 로그 백엔드가 비활성화된다. -
는 표준 출력을 의미한다.--audit-log-maxage
는 오래된 감사 로그 파일을 보관할 최대 일수를 정의한다.--audit-log-maxbackup
은 보관할 감사 로그 파일의 최대 수를 정의한다.--audit-log-maxsize
는 감사 로그 파일이 로테이트 되기 전의 최대 크기(MB)를 정의한다.클러스터의 컨트롤 플레인이 kube-apiserver를 파드로 실행하는 경우 감사 레코드가 지속되도록
정책 파일 및 로그 파일의 위치에 hostPath
를 마운트 해야한다. 예를 들면
--audit-policy-file=/etc/kubernetes/audit-policy.yaml \
--audit-log-path=/var/log/kubernetes/audit/audit.log
그런 다음 볼륨을 마운트 한다.
...
volumeMounts:
- mountPath: /etc/kubernetes/audit-policy.yaml
name: audit
readOnly: true
- mountPath: /var/log/kubernetes/audit/
name: audit-log
readOnly: false
그리고 마지막으로 hostPath
를 구성한다.
...
volumes:
- name: audit
hostPath:
path: /etc/kubernetes/audit-policy.yaml
type: File
- name: audit-log
hostPath:
path: /var/log/kubernetes/audit/
type: DirectoryOrCreate
웹훅 감사 백엔드는 원격 웹 API로 감사 이벤트를 전송하는데, 이는 인증 수단을 포함하여 쿠버네티스 API의 한 형태로 간주된다. 다음 kube-apiserver 플래그를 사용하여 웹훅 감사 백엔드를 구성할 수 있다.
--audit-webhook-config-file
은 웹훅 구성이 있는 파일의 경로를 지정한다.
웹훅 구성은 효과적으로 전문화된
kubeconfig이다.--audit-webhook-initial-backoff
는 첫 번째 실패한 요청 후 다시 시도하기 전에 대기할 시간을 지정한다.
이후 요청은 지수의 백오프로 재시도 된다.웹훅 구성 파일은 kubeconfig 형식을 사용하여 서비스의 원격 주소와 서비스에 연결하는 데 사용되는 자격 증명을 지정한다.
로그 및 웹 훅 백엔드는 모두 배치를 지원한다. 웹훅 사용을 예로 들면, 다음은 사용 가능한 플래그 목록이다.
로그 백엔드에 대해 동일한 플래그를 가져오려면 플래그 이름에 있는 webhook
을 log
로 바꾼다.
기본적으로 배치 기능은 webhook
에서 활성화되고 log
에서 비활성화된다. 마찬가지로, 기본적으로
쓰로틀링은 webhook
에서는 활성화되고 log
에서는 비활성화된다.
--audit-webhook-mode
은 버퍼링 전략을 정의한다. 다음 중 하나이다.
batch
- 이벤트를 버퍼링하고 비동기식으로 배치한다. 이것이 기본값이다.blocking
- 각 개별 이벤트를 처리할 때 API 서버 응답을 차단한다.blocking-strict
- blocking
과 동일하지만, RequestReceived 단계에서 감사 로깅 중에 오류가 발생하면
kube-apiserver에 대한 전체 요청이 실패한다.다음 플래그는 batch
모드에서만 사용된다.
--audit-webhook-batch-buffer-size
는 배치하기 전에 버퍼링할 이벤트 수를 정의한다.
들어오는 이벤트의 비율이 버퍼를 초과하면 이벤트가 삭제된다.--audit-webhook-batch-max-size
는 한 배치의 최대 이벤트 수를 정의한다.--audit-webhook-batch-max-wait
는 대기열에서 이벤트를 무조건 배치하기 전에
대기할 최대 시간을 정의한다.--audit-webhook-batch-throttle-qps
는 초당 생성되는 최대 평균 배치 수를
정의한다.--audit-webhook-batch-throttle-burst
는 허용된 QPS가 이전에 충분히 활용되지 않은 경우
동시에 생성되는 최대 배치 수를 정의한다.파라미터는 API 서버의 로드를 수용할 수 있도록 설정해야 한다.
예를 들어 kube-apiserver가 초당 100건의 요청을 수신하고 각 요청이
ResponseStarted
와 ResponseComplete
단계에서만 감사되는 경우 초당 생성되는 ≅200건의 감사 이벤트를 고려해야 한다.
일괄적으로 최대 100개의 이벤트가 있다고 가정할 때
초당 최소 2개의 쿼리 조절 수준을 설정해야 한다.
백엔드가 이벤트를 쓰는 데 최대 5초가 걸릴 수 있다고 가정하면 버퍼크기를 최대 5초의 이벤트를 보유하도록 설정해야 한다.
즉, 10개의 배치 또는 100개의 이벤트이다.
그러나 대부분의 경우 기본 매개 변수만 있으면 충분하며 수동으로 설정할 필요가 없다. kube-apiserver에서 노출된 다음과 같은 프로메테우스 메트릭과 로그에서 감사 하위 시스템의 상태를 모니터링할 수 있다.
apiserver_audit_event_total
의 메트릭에는 내보낸 감사 이벤트의 총 수가 포함된다.apiserver_audit_error_total
의 메트릭에는 내보내기 중 오류로 인해 삭제된 총 이벤트
수가 포함된다.로그 및 웹훅 백엔드는 모두 로깅되는 이벤트의 크기 제한을 지원한다. 예를 들어 로그 백엔드에 사용할 수 있는 플래그 목록은 다음과 같다.
audit-log-truncate-enabled
는 이벤트 및 자르기 배치가 활성화 되었는지 여부를 나타낸다.audit-log-truncate-max-batch-size
는 기본 백엔드로 전송되는 배치의 바이트 단위의 최대 크기이다.audit-log-truncate-max-event-size
는 기본 백엔드로 전송된 감사 이벤트의 바이트 단위의 최대 크기이다.기본적으로 webhook
과 log
모두에서 자르기 기능이 비활성화되어 있으므로 이 기능을 활성화 하기 위해
클러스터 관리자는 audit-log-truncate-enabled
또는 audit-webhook-truncate-enabled
를 설정해야 한다.
Event
에 대해 알아보고
감사 구성 참조를 읽고 Policy
리소스 유형 확인하기.쿠버네티스 애플리케이션은 일반적으로 각각 자체 컨테이너에서 실행되는 여러 개의 개별 서비스로 구성된다. 원격 쿠버네티스 클러스터 상에서 이러한 서비스를 개발하고 디버깅하려면 디버깅 도구를 실행하기 위해 동작 중인 컨테이너의 셸(shell)에 접근해야 하기 때문에 번거로울 수 있다.
텔레프레즌스(telepresence)
는 원격 쿠버네티스 클러스터로 서비스를 프록시하면서 로컬에서 서비스를 개발 및 디버깅하는 과정을 용이하게 하는 도구이다. 텔레프레즌스
를 사용하면 로컬 서비스에 디버거 및 IDE와 같은 사용자 지정 도구를 사용할 수 있고 원격 클러스터에서 실행되는 컨피그맵(ConfigMap), 시크릿(Secret) 및 서비스(Service)에 대한 전체 접근 권한을 서비스에 제공할 수 있다.
이 문서는 텔레프레즌스
를 사용하여 원격 클러스터에서 실행 중인 서비스를 로컬로 개발하고 디버그하는 방법을 설명한다.
kubectl
은 클러스터와 통신하도록 구성되어 있어야 한다.텔레프레즌스
를 설치한 후 텔레프레즌스 커넥트(telepresence connect)
를 실행하여 데몬을 실행하고 로컬 워크스테이션을 클러스터에 연결한다.
$ telepresence connect
Launching Telepresence Daemon
...
Connected to context default (https://<cluster public IP>)
쿠버네티스 구문을 사용하여 서비스에 curl이 가능하다. 예, curl -ik https://kubernetes.default
쿠버네티스에서 애플리케이션을 개발할 때 일반적으로 단일 서비스를 프로그래밍하거나 디버그한다. 서비스를 테스트 및 디버깅하기 위해 다른 서비스에 접근이 필요할 수 있다. 지속적 배포(continuous deployment) 파이프라인을 사용하는 것도 한 가지 옵션이지만, 가장 빠른 배포 파이프라인이라도 프로그래밍 또는 디버그 주기에 지연이 발생할 수 있다.
telepresence intercept $SERVICE_NAME --port $LOCAL_PORT:$REMOTE_PORT
명령을 사용하여 원격 서비스 트래픽을 다시 라우팅하기 위한 "인터셉트(intercept)"를 생성한다.
아래의 각 항목에 대한 설명을 참고한다.
$SERVICE_NAME
은 로컬 서비스의 이름이다.$LOCAL_PORT
는 서비스가 로컬 워크스테이션에서 실행 중인 포트이다.$REMOTE_PORT
는 서비스가 클러스터에서 수신하는 포트이다.이 명령을 실행하면 원격 쿠버네티스 클러스터의 서비스 대신 로컬 서비스에 원격 트래픽을 보내도록 텔레프레즌스에 지시한다. 서비스 소스 코드를 로컬에서 편집하고 저장하여 원격 애플리케이션에 접근할 때 해당 변경 사항이 즉시 적용되는지 확인한다. 디버거 또는 기타 로컬 개발 도구를 사용하여 로컬 서비스를 실행할 수도 있다.
Telepresence는 원격 클러스터에서 실행 중인 기존 애플리케이션의 컨테이너 옆에 트래픽 에이전트 사이드카(sidecar)를 설치한다. 그런 다음 파드로 들어오는 모든 트래픽 요청을 캡처하고, 이를 원격 클러스터의 애플리케이션에 전달하는 대신, 모든 트래픽(글로벌 인터셉트를 생성하는 경우) 또는 일부 트래픽(개인 인터셉트를 생성하는 경우)을 로컬 개발 환경으로 라우팅한다.
핸즈온 튜토리얼에 관심이 있다면 구글 쿠버네티스 엔진 상의 방명록 애플리케이션을 로컬로 개발하는 과정을 안내하는 이 튜토리얼을 확인한다.
자세한 내용은 텔레프레즌스 웹사이트를 참조한다.
내 파드가 "Container Creating"에서 멈췄거나 계속해서 다시 시작된다.
퍼즈(pause) 이미지가 OS 버전과 호환되는지 확인한다. 퍼즈 컨테이너에서 최신 / 추천 퍼즈 이미지 및 추가 정보를 확인한다.
config.toml
환경 설정 파일의 plugins.plugins.cri.sandbox_image
필드에 명시되어 있다.내 파드의 상태가 ErrImgPull
또는 ImagePullBackOff
이다.
파드가 호환되는 윈도우 노드에 스케줄링되었는지 확인한다.
각 파드와 호환되는 노드를 찾는 방법에 대한 추가 정보는 이 가이드를 참고한다.
내 윈도우 파드에 네트워크 연결이 없다.
가상 머신을 사용하는 경우, 모든 VM 네트워크 어댑터에 MAC 스푸핑이 활성화되어 있는지 확인한다.
내 윈도우 파드가 외부 리소스를 ping 할 수 없다.
윈도우 파드에는 현재 ICMP 프로토콜용으로 프로그래밍된 아웃바운드 규칙이 없다.
그러나 TCP/UDP는 지원된다.
클러스터 외부 리소스에 대한 연결을 시연하려는 경우,
ping <IP>
를 대응되는 curl <IP>
명령으로 대체한다.
여전히 문제가 발생하는 경우, cni.conf의 네트워크 구성에 특별히 추가 확인이 필요하다. 언제든지 이 정적 파일을 편집할 수 있다. 구성 업데이트는 새로 생성된 모든 쿠버네티스 리소스에 적용된다.
쿠버네티스 네트워킹 요구
사항(쿠버네티스 모델 참조)
중 하나는
클러스터 통신이 NAT 없이 내부적으로 발생해야 한다는 것이다.
이 요구 사항을 준수하기 위해
아웃바운드 NAT가 발생하지 않도록 하는
모든 통신에 대한 ExceptionList가 있다.
그러나 이것은 쿼리하려는 외부 IP를 ExceptionList
에서 제외해야 함도 의미한다.
그래야만 윈도우 파드에서 발생하는 트래픽이 제대로 SNAT 되어 외부에서 응답을 받는다.
이와 관련하여 cni.conf
의 ExceptionList
는 다음과 같아야 한다.
"ExceptionList": [
"10.244.0.0/16", # 클러스터 서브넷
"10.96.0.0/12", # 서비스 서브넷
"10.127.130.0/24" # 관리(호스트) 서브넷
]
내 윈도우 노드가 NodePort
서비스에 접근할 수 없다.
노드 자체에서는 로컬 NodePort 접근이 실패한다. 이것은 알려진 제약사항이다. NodePort 접근은 다른 노드 또는 외부 클라이언트에서는 가능하다.
컨테이너의 vNIC 및 HNS 엔드포인트가 삭제된다.
이 문제는 hostname-override
파라미터가
kube-proxy에 전달되지 않은 경우 발생할 수 있다.
이를 해결하려면 사용자는 다음과 같이 hostname을 kube-proxy에 전달해야 한다.
C:\k\kube-proxy.exe --hostname-override=$(hostname)
내 윈도우 노드가 서비스 IP를 사용하여 내 서비스에 접근할 수 없다.
이는 윈도우에서 현재 네트워킹 스택의 알려진 제약 사항이다. 그러나 윈도우 파드는 서비스 IP에 접근할 수 있다.
kubelet을 시작할 때 네트워크 어댑터를 찾을 수 없다.
윈도우 네트워킹 스택에는 쿠버네티스 네트워킹이 작동하기 위한 가상 어댑터가 필요하다. (어드민 셸에서) 다음 명령이 결과를 반환하지 않으면, Kubelet이 작동하는 데 필요한 필수 구성 요소인 가상 네트워크 생성이 실패한 것이다.
Get-HnsNetwork | ? Name -ieq "cbr0"
Get-NetAdapter | ? Name -Like "vEthernet (Ethernet*"
호스트 네트워크 어댑터가 "Ethernet"이 아닌 경우, 종종 start.ps1
스크립트의
InterfaceName 파라미터를 수정하는 것이 좋다.
그렇지 않으면 start-kubelet.ps1
스크립트의 출력을 참조하여 가상 네트워크 생성 중에 오류가 있는지 확인한다.
DNS 해석(resolution)이 제대로 작동하지 않는다.
이 섹션에서 윈도우에서의 DNS 제한을 확인한다.
kubectl port-forward
가 "unable to do port forwarding: wincat not found" 에러와 함께 실패한다.
이 기능은 퍼즈 인프라 컨테이너 mcr.microsoft.com/oss/kubernetes/pause:3.6
에
wincat.exe
를 포함시킴으로써 쿠버네티스 1.15에서 구현되었다.
지원되는 쿠버네티스 버전을 사용하고 있는지 확인한다.
퍼즈 인프라 컨테이너를 직접 빌드하려면
wincat을 포함시켜야 한다.
내 윈도우 서버 노드가 프록시 뒤에 있기 때문에 쿠버네티스 설치에 실패한다.
프록시 뒤에 있는 경우, 다음 PowerShell 환경 변수를 정의해야 한다.
[Environment]::SetEnvironmentVariable("HTTP_PROXY", "http://proxy.example.com:80/", [EnvironmentVariableTarget]::Machine)
[Environment]::SetEnvironmentVariable("HTTPS_PROXY", "http://proxy.example.com:443/", [EnvironmentVariableTarget]::Machine)
플란넬(flannel)을 사용하면 클러스터에 다시 조인(join)한 후 노드에 이슈가 발생한다.
이전에 삭제된 노드가 클러스터에 다시 조인될 때마다, flannelD는 새 파드 서브넷을 노드에 할당하려고 한다. 사용자는 다음 경로에서 이전 파드 서브넷 구성 파일을 제거해야 한다.
Remove-Item C:\k\SourceVip.json
Remove-Item C:\k\SourceVipRequest.json
flanneld가 "Waiting for the Network to be created" 상태에서 멈춘다.
이 이슈에 대한 수많은 보고가 있다. 플란넬 네트워크의 관리 IP가 설정될 때의 타이밍 이슈일 가능성이 높다. 해결 방법은 start.ps1을 다시 시작하거나 다음과 같이 수동으로 다시 시작하는 것이다.
[Environment]::SetEnvironmentVariable("NODE_NAME", "<Windows_Worker_Hostname>")
C:\flannel\flanneld.exe --kubeconfig-file=c:\k\config --iface=<Windows_Worker_Node_IP> --ip-masq=1 --kube-subnet-mgr=1
/run/flannel/subnet.env
누락으로 인해 윈도우 파드를 시작할 수 없다.
이것은 플란넬이 제대로 실행되지 않았음을 나타낸다.
flanneld.exe
를 다시 시작하거나
쿠버네티스 마스터의 /run/flannel/subnet.env
에서 윈도우 워커 노드의 C:\run\flannel\subnet.env
로 파일을 수동으로 복사할 수 있고,
FLANNEL_SUBNET
행을 다른 숫자로 수정한다.
예를 들어, 노드 서브넷 10.244.4.1/24가 필요한 경우 다음과 같이 설정한다.
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.4.1/24
FLANNEL_MTU=1500
FLANNEL_IPMASQ=true
이러한 단계로 문제가 해결되지 않으면, 다음을 통해 쿠버네티스의 윈도우 노드에서 윈도우 컨테이너를 실행하는 데 도움을 받을 수 있다.
Kubernetes v1.15 [stable]
kubeadm으로 생성된 클라이언트 인증서는 1년 후에 만료된다. 이 페이지는 kubeadm으로 인증서 갱신을 관리하는 방법을 설명하며, kubeadm 인증서 관리와 관련된 다른 작업에 대해서도 다룬다.
쿠버네티스의 PKI 인증서와 요구 조건에 익숙해야 한다.
기본적으로, kubeadm은 클러스터를 실행하는 데 필요한 모든 인증서를 생성한다. 사용자는 자체 인증서를 제공하여 이 동작을 무시할 수 있다.
이렇게 하려면, --cert-dir
플래그 또는 kubeadm ClusterConfiguration
의
certificatesDir
필드에 지정된 디렉터리에 배치해야 한다.
기본적으로 /etc/kubernetes/pki
이다.
kubeadm init
을 실행하기 전에 지정된 인증서와 개인 키(private key) 쌍이 존재하면,
kubeadm은 이를 덮어 쓰지 않는다. 이는 예를 들어, 기존 CA를
/etc/kubernetes/pki/ca.crt
와 /etc/kubernetes/pki/ca.key
에
복사할 수 있고, kubeadm은 이 CA를 사용하여 나머지 인증서에 서명한다는 걸 의미한다.
ca.key
파일이 아닌 ca.crt
파일만 제공할
수도 있다(이는 다른 인증서 쌍이 아닌 루트 CA 파일에만 사용 가능함).
다른 모든 인증서와 kubeconfig 파일이 있으면, kubeadm은 이 조건을
인식하고 "외부 CA" 모드를 활성화한다. kubeadm은 디스크에
CA 키없이 진행한다.
대신, --controllers=csrsigner
사용하여 controller-manager를
독립적으로 실행하고 CA 인증서와 키를 가리킨다.
PKI 인증서와 요구 조건은 외부 CA를 사용하도록 클러스터 설정에 대한 지침을 포함한다.
check-expiration
하위 명령을 사용하여 인증서가 만료되는 시기를 확인할 수 있다.
kubeadm certs check-expiration
출력 결과는 다음과 비슷하다.
CERTIFICATE EXPIRES RESIDUAL TIME CERTIFICATE AUTHORITY EXTERNALLY MANAGED
admin.conf Dec 30, 2020 23:36 UTC 364d no
apiserver Dec 30, 2020 23:36 UTC 364d ca no
apiserver-etcd-client Dec 30, 2020 23:36 UTC 364d etcd-ca no
apiserver-kubelet-client Dec 30, 2020 23:36 UTC 364d ca no
controller-manager.conf Dec 30, 2020 23:36 UTC 364d no
etcd-healthcheck-client Dec 30, 2020 23:36 UTC 364d etcd-ca no
etcd-peer Dec 30, 2020 23:36 UTC 364d etcd-ca no
etcd-server Dec 30, 2020 23:36 UTC 364d etcd-ca no
front-proxy-client Dec 30, 2020 23:36 UTC 364d front-proxy-ca no
scheduler.conf Dec 30, 2020 23:36 UTC 364d no
CERTIFICATE AUTHORITY EXPIRES RESIDUAL TIME EXTERNALLY MANAGED
ca Dec 28, 2029 23:36 UTC 9y no
etcd-ca Dec 28, 2029 23:36 UTC 9y no
front-proxy-ca Dec 28, 2029 23:36 UTC 9y no
이 명령은 /etc/kubernetes/pki
폴더의 클라이언트 인증서와 kubeadm이 사용하는 KUBECONFIG 파일(admin.conf
, controller-manager.conf
및 scheduler.conf
)에 포함된 클라이언트 인증서의 만료/잔여 기간을 표시한다.
또한, kubeadm은 인증서가 외부에서 관리되는지를 사용자에게 알린다. 이 경우 사용자는 수동으로 또는 다른 도구를 사용해서 인증서 갱신 관리를 해야 한다.
kubeadm
은 외부 CA가 서명한 인증서를 관리할 수 없다.kubelet.conf
는 위 목록에 포함되어 있지 않은데, 이는
kubeadm이 자동 인증서 갱신을 위해
/var/lib/kubelet/pki
에 있는 갱신 가능한 인증서를 이용하여 kubelet을 구성하기 때문이다.
만료된 kubelet 클라이언트 인증서를 갱신하려면
kubelet 클라이언트 갱신 실패 섹션을 확인한다.kubeadm 1.17 이전의 버전에서 kubeadm init
으로 작성된 노드에는
kubelet.conf
의 내용을 수동으로 수정해야 하는 버그가 있다. kubeadm init
수행 완료 후, client-certificate-data
및 client-key-data
를 다음과 같이 교체하여,
로테이트된 kubelet 클라이언트 인증서를 가리키도록 kubelet.conf
를 업데이트해야 한다.
client-certificate: /var/lib/kubelet/pki/kubelet-client-current.pem
client-key: /var/lib/kubelet/pki/kubelet-client-current.pem
kubeadm은 컨트롤 플레인 업그레이드 동안 모든 인증서를 갱신한다.
이 기능은 가장 간단한 유스케이스를 해결하기 위해 설계되었다. 인증서 갱신에 대해 특별한 요구 사항이 없고 쿠버네티스 버전 업그레이드를 정기적으로(매 1년 이내 업그레이드 수행) 수행하는 경우, kubeadm은 클러스터를 최신 상태로 유지하고 합리적으로 보안을 유지한다.
인증서 갱신에 대해 보다 복잡한 요구 사항이 있는 경우, --certificate-renewal=false
를 kubeadm upgrade apply
또는 kubeadm upgrade node
와 함께 사용하여 기본 동작이 수행되지 않도록 할 수 있다.
kubeadm upgrade node
명령에서
--certificate-renewal
의 기본값이 false
인 버그가
있다. 이 경우 --certificate-renewal=true
를 명시적으로 설정해야 한다.kubeadm certs renew
명령을 사용하여 언제든지 인증서를 수동으로 갱신할 수 있다.
이 명령은 /etc/kubernetes/pki
에 저장된 CA(또는 프론트 프록시 CA) 인증서와 키를 사용하여 갱신을 수행한다.
명령을 실행한 후에는 컨트롤 플레인 파드를 재시작해야 한다.
이는 현재 일부 구성 요소 및 인증서에 대해 인증서를 동적으로 다시 로드하는 것이 지원되지 않기 때문이다.
스태틱(static) 파드는 API 서버가 아닌 로컬 kubelet에서 관리되므로
kubectl을 사용하여 삭제 및 재시작할 수 없다.
스태틱 파드를 다시 시작하려면 /etc/kubernetes/manifests/
에서 매니페스트 파일을 일시적으로 제거하고
20초를 기다리면 된다 (KubeletConfiguration struct의 fileCheckFrequency
값을 참고한다).
파드가 매니페스트 디렉터리에 더 이상 없는 경우 kubelet은 파드를 종료한다.
그런 다음 파일을 다시 이동할 수 있으며 또 다른 fileCheckFrequency
기간이 지나면,
kubelet은 파드를 생성하고 구성 요소에 대한 인증서 갱신을 완료할 수 있다.
certs renew
는 기존 인증서를 kubeadm-config 컨피그맵(ConfigMap) 대신 속성(공통 이름, 조직, SAN 등)의 신뢰할 수 있는 소스로 사용한다. 둘 다 동기화 상태를 유지하는 것을 강력히 권장한다.kubeadm certs renew
는 다음의 옵션을 제공한다.
쿠버네티스 인증서는 일반적으로 1년 후 만료일에 도달한다.
--csr-only
는 실제로 인증서를 갱신하지 않고 인증서 서명 요청을 생성하여 외부 CA로 인증서를 갱신하는 데 사용할 수 있다. 자세한 내용은 다음 단락을 참고한다.
모든 인증서 대신 단일 인증서를 갱신할 수도 있다.
이 섹션에서는 쿠버네티스 인증서 API를 사용하여 수동 인증서 갱신을 실행하는 방법에 대한 자세한 정보를 제공한다.
쿠버네티스 인증 기관(Certificate Authority)은 기본적으로 작동하지 않는다. cert-manager와 같은 외부 서명자를 설정하거나, 빌트인 서명자를 사용할 수 있다.
빌트인 서명자는 kube-controller-manager
의 일부이다.
빌트인 서명자를 활성화하려면, --cluster-signing-cert-file
와 --cluster-signing-key-file
플래그를 전달해야 한다.
새 클러스터를 생성하는 경우, kubeadm 구성 파일을 사용할 수 있다.
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
controllerManager:
extraArgs:
cluster-signing-cert-file: /etc/kubernetes/pki/ca.crt
cluster-signing-key-file: /etc/kubernetes/pki/ca.key
쿠버네티스 API로 CSR을 작성하려면 CertificateSigningRequest 생성을 본다.
이 섹션에서는 외부 CA를 사용하여 수동 인증서 갱신을 실행하는 방법에 대한 자세한 정보를 제공한다.
외부 CA와 보다 효과적으로 통합하기 위해 kubeadm은 인증서 서명 요청(CSR)을 생성할 수도 있다. CSR은 클라이언트의 서명된 인증서에 대한 CA 요청을 나타낸다. kubeadm 관점에서, 일반적으로 온-디스크(on-disk) CA에 의해 서명되는 모든 인증서는 CSR로 생성될 수 있다. 그러나 CA는 CSR로 생성될 수 없다.
kubeadm certs renew --csr-only
로 인증서 서명 요청을 만들 수 있다.
CSR과 함께 제공되는 개인 키가 모두 출력된다.
--csr-dir
로 사용할 디텍터리를 전달하여 지정된 위치로 CSR을 출력할 수 있다.
--csr-dir
을 지정하지 않으면, 기본 인증서 디렉터리(/etc/kubernetes/pki
)가 사용된다.
kubeadm certs renew --csr-only
로 인증서를 갱신할 수 있다.
kubeadm init
과 마찬가지로 출력 디렉터리를 --csr-dir
플래그로 지정할 수 있다.
CSR에는 인증서 이름, 도메인 및 IP가 포함되지만, 용도를 지정하지는 않는다. 인증서를 발행할 때 올바른 인증서 용도를 지정하는 것은 CA의 책임이다.
openssl
의 경우
openssl ca
명령으로 수행한다.cfssl
의 경우
설정 파일에 용도를 지정한다.선호하는 방법으로 인증서에 서명한 후, 인증서와 개인 키를 PKI 디렉터리(기본적으로 /etc/kubernetes/pki
)에 복사해야 한다.
Kubeadm은 CA 인증서의 순환이나 교체 기능을 기본적으로 지원하지 않는다.
CA의 수동 순환이나 교체에 대한 보다 상세한 정보는 CA 인증서 수동 순환 문서를 참조한다.
기본적으로 kubeadm에 의해서 배포된 kubelet 인증서는 자가 서명된(self-signed) 것이다. 이것은 metrics-server와 같은 외부 서비스의 kubelet에 대한 연결은 TLS로 보안되지 않음을 의미한다.
제대로 서명된 인증서를 얻기 위해서 신규 kubeadm 클러스터의 kubelet을 구성하려면
다음의 최소 구성을 kubeadm init
에 전달해야 한다.
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
serverTLSBootstrap: true
만약 이미 클러스터를 생성했다면 다음을 따라 이를 조정해야 한다.
kube-system
네임스페이스에서 kubelet-config-1.31
컨피그맵을 찾아서 수정한다.
해당 컨피그맵에는 kubelet
키가
KubeletConfiguration
문서를 값으로 가진다. serverTLSBootstrap: true
가 되도록 KubeletConfiguration 문서를 수정한다.serverTLSBootstrap: true
필드를 /var/lib/kubelet/config.yaml
에 추가한다.
그리고 systemctl restart kubelet
로 kubelet을 재시작한다.serverTLSBootstrap: true
필드는 kubelet 인증서를 이용한 부트스트랩을
certificates.k8s.io
API에 요청함으로써 활성화할 것이다. 한 가지 알려진 제약은
이 인증서들에 대한 CSR(인증서 서명 요청)들이 kube-controller-manager -
kubernetes.io/kubelet-serving
의
기본 서명자(default signer)에 의해서 자동으로 승인될 수 없다는 점이다.
이것은 사용자나 제 3의 컨트롤러의 액션을 필요로 할 것이다.
이 CSR들은 다음을 통해 볼 수 있다.
kubectl get csr
NAME AGE SIGNERNAME REQUESTOR CONDITION
csr-9wvgt 112s kubernetes.io/kubelet-serving system:node:worker-1 Pending
csr-lz97v 1m58s kubernetes.io/kubelet-serving system:node:control-plane-1 Pending
이를 승인하기 위해서는 다음을 수행한다.
kubectl certificate approve <CSR-name>
기본적으로, 이 인증서는 1년 후에 만기될 것이다. Kubeadm은
KubeletConfiguration
필드의 rotateCertificates
를 true
로 설정한다. 이것은 만기가
다가오면 인증서를 위한 신규 CSR 세트가 생성되는 것을 의미하며,
해당 순환(rotation)을 완료하기 위해서는 승인이 되어야 한다는 것을 의미한다. 더 상세한 이해를 위해서는
인증서 순환를 확인한다.
만약 이 CSR들의 자동 승인을 위한 솔루션을 찾고 있다면 클라우드 제공자와 연락하여 대역 외 메커니즘(out of band mechanism)을 통해 노드의 신분을 검증할 수 있는 CSR 서명자를 가지고 있는지 문의하는 것을 추천한다.
써드파티 커스텀 컨트롤러도 사용될 수 있다.
이러한 컨트롤러는 CSR의 CommonName과 요청된 IPs 및 도메인 네임을 모두 검증하지 않는 한, 보안이 되는 메커니즘이 아니다. 이것을 통해 악의적 행위자가 kubelet 인증서(클라이언트 인증)를 사용하여 아무 IP나 도메인 네임에 대해 인증서를 요청하는 CSR의 생성을 방지할 수 있을 것이다.
클러스터 생성 과정에서, kubeadm은
Subject: O = system:masters, CN = kubernetes-admin
값이 설정되도록 admin.conf
의 인증서에 서명한다.
system:masters
는
인증 계층(예: RBAC)을 우회하는 획기적인 슈퍼유저 그룹이다.
admin.conf
를 추가 사용자와 공유하는 것은 권장하지 않는다!
대신, kubeadm kubeconfig user
명령어를 사용하여 추가 사용자를 위한 kubeconfig 파일을 생성할 수 있다.
이 명령어는 명령줄 플래그와
kubeadm 환경 설정 옵션을 모두 인식한다.
생성된 kubeconfig는 stdout으로 출력되며,
kubeadm kubeconfig user ... > somefile.conf
명령어를 사용하여 파일에 기록될 수 있다.
다음은 --config
플래그와 함께 사용될 수 있는 환경 설정 파일 예시이다.
# example.yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
# kubeconfig에서 타겟 "cluster"로 사용될 것이다.
clusterName: "kubernetes"
# kubeconfig에서 클러스터의 "server"(IP 또는 DNS 이름)로 사용될 것이다.
controlPlaneEndpoint: "some-dns-address:6443"
# 클러스터 CA 키 및 인증서가 이 로컬 디렉토리에서 로드될 것이다.
certificatesDir: "/etc/kubernetes/pki"
이러한 항목들이 사용하고자 하는 클러스터의 상세 사항과 일치하는지 확인한다. 기존 클러스터의 환경 설정을 보려면 다음 명령을 사용한다.
kubectl get cm kubeadm-config -n kube-system -o=jsonpath="{.data.ClusterConfiguration}"
다음 예시는 appdevs
그룹의 새 사용자 johndoe
를 위해
24시간동안 유효한 인증서와 함께 kubeconfig 파일을 생성할 것이다.
kubeadm kubeconfig user --config example.yaml --org appdevs --client-name johndoe --validity-period 24h
다음 예시는 1주일간 유효한 관리자 크리덴셜을 갖는 kubeconfig 파일을 생성할 것이다.
kubeadm kubeconfig user --config example.yaml --client-name admin --validity-period 168h
이 페이지는 kubeadm으로 생성된 쿠버네티스 클러스터를 1.30.x 버전에서 1.31.x 버전으로,
1.31.x 버전에서 1.31.y(여기서 y > x
) 버전으로 업그레이드하는 방법을 설명한다.
업그레이드가 지원되지 않는 경우 마이너 버전을 건너뛴다.
더 자세한 정보는 버전 차이(skew) 정책을 참고한다.
이전 버전의 kubeadm을 사용하여 생성된 클러스터 업그레이드에 대한 정보를 보려면, 이 페이지 대신 다음의 페이지들을 참고한다.
추상적인 업그레이드 작업 절차는 다음과 같다.
kubeadm upgrade
는 워크로드에 영향을 미치지 않고, 쿠버네티스 내부의 컴포넌트만 다루지만, 백업은 항상 모범 사례일 정도로 중요하다.systemctl status kubelet
명령을 실행하거나, journalctl -xeu kubelet
명령을 실행하여 서비스 로그를 확인할 수 있다.kubeadm upgrade
시에
kubeadm 구성 API 종류를 명시하여
--config
플래그를 사용하는 것은 추천하지 않으며 예상치 못한 결과를 초래할 수 있다.
대신 kubeadm 클러스터 재구성하기를 참조한다.OS 패키지 관리자를 사용하여 쿠버네티스의 최신 패치 릴리스 버전(1.31)을 찾는다.
apt update
apt-cache madison kubeadm
# 목록에서 최신 버전(1.31)을 찾는다
# 1.31.x-00과 같아야 한다. 여기서 x는 최신 패치이다.
yum list --showduplicates kubeadm --disableexcludes=kubernetes
# 목록에서 최신 버전(1.31)을 찾는다
# 1.31.x-0과 같아야 한다. 여기서 x는 최신 패치이다.
컨트롤 플레인 노드의 업그레이드 절차는 한 번에 한 노드씩 실행해야 한다.
먼저 업그레이드할 컨트롤 플레인 노드를 선택한다. /etc/kubernetes/admin.conf
파일이 있어야 한다.
첫 번째 컨트롤 플레인 노드의 경우
kubeadm 업그레이드
# 1.31.x-00에서 x를 최신 패치 버전으로 바꾼다.
apt-mark unhold kubeadm && \
apt-get update && apt-get install -y kubeadm=1.31.x-00 && \
apt-mark hold kubeadm
# 1.31.x-0에서 x를 최신 패치 버전으로 바꾼다.
yum install -y kubeadm-1.31.x-0 --disableexcludes=kubernetes
다운로드하려는 버전이 잘 받아졌는지 확인한다.
kubeadm version
업그레이드 계획을 확인한다.
kubeadm upgrade plan
이 명령은 클러스터를 업그레이드할 수 있는지를 확인하고, 업그레이드할 수 있는 버전을 가져온다. 또한 컴포넌트 구성 버전 상태가 있는 표를 보여준다.
kubeadm upgrade
는 이 노드에서 관리하는 인증서를 자동으로 갱신한다.
인증서 갱신을 하지 않으려면 --certificate-renewal=false
플래그를 사용할 수 있다.
자세한 내용은 인증서 관리 가이드를 참고한다.kubeadm upgrade plan
이 수동 업그레이드가 필요한 컴포넌트 구성을 표시하는 경우, 사용자는
--config
커맨드 라인 플래그를 통해 대체 구성이 포함된 구성 파일을 kubeadm upgrade apply
에 제공해야 한다.
그렇게 하지 않으면 kubeadm upgrade apply
가 오류와 함께 종료되고 업그레이드를 수행하지 않는다.업그레이드할 버전을 선택하고, 적절한 명령을 실행한다. 예를 들면 다음과 같다.
# 이 업그레이드를 위해 선택한 패치 버전으로 x를 바꾼다.
sudo kubeadm upgrade apply v1.31.x
명령이 완료되면 다음을 확인해야 한다.
[upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.31.x". Enjoy!
[upgrade/kubelet] Now that your control plane is upgraded, please proceed with upgrading your kubelets if you haven't already done so.
CNI 제공자 플러그인을 수동으로 업그레이드한다.
CNI(컨테이너 네트워크 인터페이스) 제공자는 자체 업그레이드 지침을 따를 수 있다. 애드온 페이지에서 사용하는 CNI 제공자를 찾고 추가 업그레이드 단계가 필요한지 여부를 확인한다.
CNI 제공자가 데몬셋(DaemonSet)으로 실행되는 경우 추가 컨트롤 플레인 노드에는 이 단계가 필요하지 않다.
다른 컨트롤 플레인 노드의 경우
첫 번째 컨트롤 플레인 노드와 동일하지만 다음을 사용한다.
sudo kubeadm upgrade node
아래 명령 대신 위의 명령을 사용한다.
sudo kubeadm upgrade apply
kubeadm upgrade plan
을 호출하고 CNI 공급자 플러그인을 업그레이드할 필요가 없다.
스케줄 불가능(unschedulable)으로 표시하고 워크로드를 축출하여 유지 보수할 노드를 준비한다.
# <node-to-drain>을 드레인하는 노드의 이름으로 바꾼다.
kubectl drain <node-to-drain> --ignore-daemonsets
모든 컨트롤 플레인 노드에서 kubelet 및 kubectl을 업그레이드한다.
# replace x in 1.31.x-00의 x를 최신 패치 버전으로 바꾼다
apt-mark unhold kubelet kubectl && \
apt-get update && apt-get install -y kubelet=1.31.x-00 kubectl=1.31.x-00 && \
apt-mark hold kubelet kubectl
# 1.31.x-0에서 x를 최신 패치 버전으로 바꾼다
yum install -y kubelet-1.31.x-0 kubectl-1.31.x-0 --disableexcludes=kubernetes
kubelet을 다시 시작한다.
sudo systemctl daemon-reload
sudo systemctl restart kubelet
노드를 스케줄 가능(schedulable)으로 표시하여 노드를 다시 온라인 상태로 전환한다.
# <node-to-uncordon>을 드레인하려는 노드의 이름으로 바꾼다.
kubectl uncordon <node-to-uncordon>
워커 노드의 업그레이드 절차는 워크로드를 실행하는 데 필요한 최소 용량을 보장하면서, 한 번에 하나의 노드 또는 한 번에 몇 개의 노드로 실행해야 한다.
모든 워커 노드에서 kubeadm을 업그레이드한다.
# 1.31.x-00의 x를 최신 패치 버전으로 바꾼다
apt-mark unhold kubeadm && \
apt-get update && apt-get install -y kubeadm=1.31.x-00 && \
apt-mark hold kubeadm
# 1.31.x-0에서 x를 최신 패치 버전으로 바꾼다
yum install -y kubeadm-1.31.x-0 --disableexcludes=kubernetes
워커 노드의 경우 로컬 kubelet 구성을 업그레이드한다.
sudo kubeadm upgrade node
스케줄 불가능(unschedulable)으로 표시하고 워크로드를 축출하여 유지 보수할 노드를 준비한다.
# <node-to-drain>을 드레인하려는 노드 이름으로 바꾼다.
kubectl drain <node-to-drain> --ignore-daemonsets
kubelet 및 kubectl을 업그레이드한다.
# 1.31.x-00의 x를 최신 패치 버전으로 바꾼다
apt-mark unhold kubelet kubectl && \
apt-get update && apt-get install -y kubelet=1.31.x-00 kubectl=1.31.x-00 && \
apt-mark hold kubelet kubectl
# 1.31.x-0에서 x를 최신 패치 버전으로 바꾼다
yum install -y kubelet-1.31.x-0 kubectl-1.31.x-0 --disableexcludes=kubernetes
kubelet을 다시 시작한다.
sudo systemctl daemon-reload
sudo systemctl restart kubelet
# <node-to-uncordon>을 노드의 이름으로 바꾼다.
kubectl uncordon <node-to-uncordon>
모든 노드에서 kubelet을 업그레이드한 후 kubectl이 클러스터에 접근할 수 있는 곳에서 다음의 명령을 실행하여 모든 노드를 다시 사용할 수 있는지 확인한다.
kubectl get nodes
모든 노드에 대해 STATUS
열에 Ready
가 표시되어야 하고, 버전 번호가 업데이트되어 있어야 한다.
예를 들어 kubeadm upgrade
를 실행하는 중에 예기치 못한 종료로 인해 업그레이드가 실패하고 롤백하지 않는다면, kubeadm upgrade
를 다시 실행할 수 있다.
이 명령은 멱등성을 보장하며 결국 실제 상태가 선언한 의도한 상태인지 확인한다.
잘못된 상태에서 복구하기 위해, 클러스터가 실행 중인 버전을 변경하지 않고 kubeadm upgrade apply --force
를 실행할 수도 있다.
업그레이드하는 동안 kubeadm은 /etc/kubernetes/tmp
아래에 다음과 같은 백업 폴더를 작성한다.
kubeadm-backup-etcd-<date>-<time>
kubeadm-backup-manifests-<date>-<time>
kubeadm-backup-etcd
는 컨트롤 플레인 노드에 대한 로컬 etcd 멤버 데이터의 백업을 포함한다.
etcd 업그레이드가 실패하고 자동 롤백이 작동하지 않으면, 이 폴더의 내용을
/var/lib/etcd
에서 수동으로 복원할 수 있다. 외부 etcd를 사용하는 경우 이 백업 폴더는 비어있다.
kubeadm-backup-manifests
는 컨트롤 플레인 노드에 대한 정적 파드 매니페스트 파일의 백업을 포함한다.
업그레이드가 실패하고 자동 롤백이 작동하지 않으면, 이 폴더의 내용을
/etc/kubernetes/manifests
에서 수동으로 복원할 수 있다. 어떤 이유로 특정 컴포넌트의 업그레이드 전
매니페스트 파일과 업그레이드 후 매니페스트 파일 간에 차이가 없는 경우, 백업 파일은 기록되지 않는다.
kubeadm upgrade apply
는 다음을 수행한다.
Ready
상태에 있다CoreDNS
와 kube-proxy
매니페스트를 적용하고 필요한 모든 RBAC 규칙이 생성되도록 한다.kubeadm upgrade node
는 추가 컨트롤 플레인 노드에서 다음을 수행한다.
ClusterConfiguration
을 가져온다.kubeadm upgrade node
는 워커 노드에서 다음을 수행한다.
ClusterConfiguration
을 가져온다.Kubernetes v1.18 [beta]
이 페이지는 kubeadm으로 생성된 윈도우 노드를 업그레이드하는 방법을 설명한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: 1.17. 버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
윈도우 노드에서, kubeadm을 업그레이드한다.
# 1.31.0을 사용 중인 쿠버네티스 버전으로 변경한다.
curl.exe -Lo <kubeadm.exe을 저장할 경로> "https://dl.k8s.io/v1.31.0/bin/windows/amd64/kubeadm.exe"
쿠버네티스 API에 접근할 수 있는 머신에서, 스케줄 불가능한 것으로 표시하고 워크로드를 축출하여 유지 보수할 노드를 준비한다.
# <node-to-drain>을 드레이닝하려는 노드 이름으로 바꾼다
kubectl drain <node-to-drain> --ignore-daemonsets
다음과 비슷한 출력이 표시되어야 한다.
node/ip-172-31-85-18 cordoned
node/ip-172-31-85-18 drained
윈도우 노드에서, 다음의 명령을 호출하여 새 kubelet 구성을 동기화한다.
kubeadm upgrade node
윈도우 노드에서, kubelet을 업그레이드하고 다시 시작한다.
stop-service kubelet
curl.exe -Lo <kubelet.exe을 저장할 경로> "https://dl.k8s.io/v1.31.0/bin/windows/amd64/kubelet.exe"
restart-service kubelet
윈도우 노드에서, kube-proxy를 업그레이드하고 다시 시작한다.
stop-service kube-proxy
curl.exe -Lo <kube-proxy.exe을 저장할 경로> "https://dl.k8s.io/v1.31.0/bin/windows/amd64/kube-proxy.exe"
restart-service kube-proxy
쿠버네티스 API에 접근할 수 있는 머신에서, 스케줄 가능으로 표시하여 노드를 다시 온라인으로 가져온다.
# <node-to-drain>을 노드의 이름으로 바꾼다
kubectl uncordon <node-to-drain>
이 페이지는 kubeadm으로 생성된 리눅스 노드를 업그레이드하는 방법을 설명한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
kubeadm을 업그레이드한다.
# 1.31.x-00 에서 x 에 최신 버전을 넣는다.
apt-mark unhold kubeadm && \
apt-get update && apt-get install -y kubeadm=1.31.x-00 && \
apt-mark hold kubeadm
# 1.31.x-00 에서 x 에 최신 버전을 넣는다.
yum install -y kubeadm-1.31.x-0 --disableexcludes=kubernetes
워커 노드의 경우 로컬 kubelet 구성을 업그레이드한다.
sudo kubeadm upgrade node
노드를 스케줄 불가능한 것으로 표시하고 워크로드를 축출하여 유지 보수할 노드를 준비한다.
# <node-to-drain> 에 드레인하려는 노드의 이름을 넣는다.
kubectl drain <node-to-drain> --ignore-daemonsets
kubelet과 kubectl 업그레이드.
# 1.31.x-00 에서 x 에 최신 버전을 넣는다.
apt-mark unhold kubelet kubectl && \
apt-get update && apt-get install -y kubelet=1.31.x-00 kubectl=1.31.x-00 && \
apt-mark hold kubelet kubectl
# 1.31.x-00 에서 x 에 최신 버전을 넣는다.
yum install -y kubelet-1.31.x-0 kubectl-1.31.x-0 --disableexcludes=kubernetes
kubelet을 재시작한다.
sudo systemctl daemon-reload
sudo systemctl restart kubelet
스케줄 가능으로 표시하여 노드를 다시 온라인으로 가져온다.
# <node-to-uncordon> 에 노드의 이름을 넣는다.
kubectl uncordon <node-to-uncordon>
이 섹션은 도커심에서 다른 컨테이너 런타임으로 마이그레이션할 때에 알아야 할 정보를 제공한다.
쿠버네티스 1.20에서의 도커심 사용 중단(deprecation) 발표 이후, 이것이 다양한 워크로드와 쿠버네티스 설치에 어떻게 영향을 미칠지에 대한 질문이 많았다. 도커심 제거 FAQ는 관련된 문제를 더 잘 이해할 수 있도록 도움을 준다.
도커심은 쿠버네티스 릴리스 v1.24부터 제거되었다. 컨테이너 런타임으로 도커 엔진을 통한 도커심을 사용하는 상황에서 v1.24로 업그레이드하려는 경우, 다른 런타임으로 마이그레이션하거나 다른 방법을 찾아 도커 엔진 지원을 받는 것이 좋다. 선택 가능한 옵션은 컨테이너 런타임 섹션에서 확인한다. 마이그레이션 중 문제를 마주한다면 문제를 보고하면 좋다. 이를 통해 문제를 시기적절하게 해결할 수 있으며, 클러스터도 도커심 제거에 대비할 수 있다.
클러스터는 두 종류 이상의 노드들을 포함할 수 있지만 이는 일반적인 구성은 아니다.
다음 작업을 통해 마이그레이션을 수행할 수 있다.
이 페이지는 도커 엔진 노드가 도커심 대신 cri-dockerd
를 사용하도록 마이그레이션하는 방법을 보여 준다.
다음 시나리오에서는 아래 단계를 따라야 한다.
cri-dockerd
도 선택지 중 하나이다.도커심 제거에 관하여 더 배우려면, FAQ page를 읽어보자.
쿠버네티스 1.23 이하에서는 도커심 이라는 이름의 쿠버네티스 내장 구성요소를 사용하여
도커 엔진을 쿠버네티스 컨테이너 런타임으로 사용할 수 있었다.
도커심 구성 요소는 쿠버네티스 1.24 릴리스에서 제거되었지만,
대신 서드 파티 대체제 cri-dockerd
를 사용할 수 있다.
cri-dockerd
어댑터를 사용하면 컨테이너 런타임 인터페이스(Container runtime interface, CRI)를 통해 도커 엔진을 사용할 수 있다.
컨테이너 런타임으로 도커 엔진을 계속 사용할 수 있도록
cri-dockerd
로 마이그레이션하려는 경우
영향을 받는 각각의 노드에 아래 내용을 진행해야 한다.
cri-dockerd
를 설치한다.cri-dockerd
를 사용하도록 kubelet를 설정한다.중요하지 않은(non-critical) 노드에서 먼저 테스트한다.
cri-dockerd
로 마이그레이션하려는 각 노드에 대해
아래 단계를 수행해야 한다.
cri-dockerd
를
각 노드에 설치하고 시작한다.새로운 파드를 노드에 스케줄링하는 것을 막기 위해 노드를 통제한다.
kubectl cordon <NODE_NAME>
<NODE_NAME>
부분에 노드의 이름을 입력한다.
실행 중인 파드를 안전하게 축출하기 위해 노드를 비운다.
kubectl drain <NODE_NAME> \
--ignore-daemonsets
아래의 단계는 kubeadm 도구를 사용하여 생성된 클러스터에 적용된다. 다른 도구를 사용했다면, 해당 도구에 대한 환경 설정 방법을 참고하여 kubelet 환경 설정을 수정해야 한다.
/var/lib/kubelet/kubeadm-flags.env
를 연다.--container-runtime-endpoint
플래그를
unix:///var/run/cri-dockerd.sock
로 수정한다.kubeadm 도구는 노드의 소켓을 컨트롤 플레인의 Node
오브젝트의 어노테이션으로 저장한다.
영향을 받는 각 노드의 해당 소켓을 수정하려면 다음을 따른다.
Node
오브젝트의 YAML 표현식을 편집한다.
KUBECONFIG=/path/to/admin.conf kubectl edit no <NODE_NAME>
각 인자는 다음과 같이 입력한다.
/path/to/admin.conf
:
kubectl 환경 설정 파일(admin.conf
)의 경로.<NODE_NAME>
: 수정을 원하는 노드의 이름.kubeadm.alpha.kubernetes.io/cri-socket
의 값
/var/run/dockershim.sock
을 unix:///var/run/cri-dockerd.sock
로 변경한다.
변경을 저장한다. Node
오브젝트는 저장 시 업데이트된다.
systemctl restart kubelet
노드가 cri-dockerd
엔드포인트를 사용하는지 확인하려면,
사용 런타임 찾기 지침을 따른다.
kubelet의 --container-runtime-endpoint
플래그는 unix:///var/run/cri-dockerd.sock
이어야 한다.
노드에 파드를 스케줄 하도록 통제를 해제한다.
kubectl uncordon <NODE_NAME>
이 페이지는 네임스페이스에 대한 기본 메모리 요청량(request) 및 상한(limit)을 구성하는 방법을 보여준다.
쿠버네티스 클러스터를 여러 네임스페이스로 나눌 수 있다. 기본 메모리 상한이 설정되어 있는 네임스페이스에 파드를 생성했는데, 해당 파드의 모든 컨테이너에 메모리 상한이 명시되어 있지 않다면, 컨트롤 플레인이 해당 컨테이너에 기본 메모리 상한을 할당한다.
쿠버네티스는 이 문서의 뒷부분에서 설명하는 특정 조건에서 기본 메모리 요청량을 할당한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
클러스터에 네임스페이스를 생성할 수 있는 권한이 있어야 한다.
클러스터의 각 노드에는 최소 2GiB의 메모리가 있어야 한다.
이 연습에서 생성한 리소스가 클러스터의 다른 리소스와 격리되도록 네임스페이스를 생성한다.
kubectl create namespace default-mem-example
다음은 예시 리밋레인지에 대한 매니페스트이다. 이 매니페스트는 기본 메모리 요청량 및 기본 메모리 상한을 지정한다.
apiVersion: v1
kind: LimitRange
metadata:
name: mem-limit-range
spec:
limits:
- default:
memory: 512Mi
defaultRequest:
memory: 256Mi
type: Container
default-mem-example 네임스페이스에 리밋레인지를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults.yaml --namespace=default-mem-example
이제 파드를 default-mem-example
네임스페이스에 생성하고,
해당 파드의 어떤 컨테이너도 자체 메모리 요청량(request)과 상한(limit)을 명시하지 않으면,
컨트롤 플레인이 해당 컨테이너에 메모리 요청량의 기본값(256 MiB)과
상한의 기본값(512 MiB)을 지정한다.
다음은 컨테이너가 하나인 파드의 매니페스트이다. 해당 컨테이너는 메모리 요청량과 상한을 지정하지 않는다.
apiVersion: v1
kind: Pod
metadata:
name: default-mem-demo
spec:
containers:
- name: default-mem-demo-ctr
image: nginx
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod.yaml --namespace=default-mem-example
파드에 대한 자세한 정보를 본다.
kubectl get pod default-mem-demo --output=yaml --namespace=default-mem-example
출력 결과는 파드의 컨테이너에 256MiB의 메모리 요청량과 512MiB의 메모리 상한이 있음을 나타낸다. 이것은 리밋레인지에 의해 지정된 기본값이다.
containers:
- image: nginx
imagePullPolicy: Always
name: default-mem-demo-ctr
resources:
limits:
memory: 512Mi
requests:
memory: 256Mi
파드를 삭제한다.
kubectl delete pod default-mem-demo --namespace=default-mem-example
다음은 컨테이너가 하나인 파드의 매니페스트이다. 해당 컨테이너는 메모리 상한은 지정하지만, 요청량은 지정하지 않는다.
apiVersion: v1
kind: Pod
metadata:
name: default-mem-demo-2
spec:
containers:
- name: default-mem-demo-2-ctr
image: nginx
resources:
limits:
memory: "1Gi"
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod-2.yaml --namespace=default-mem-example
파드에 대한 자세한 정보를 본다.
kubectl get pod default-mem-demo-2 --output=yaml --namespace=default-mem-example
출력 결과는 컨테이너의 메모리 요청량이 메모리 상한과 일치하도록 설정되었음을 보여준다. 참고로 컨테이너에는 기본 메모리 요청량의 값인 256Mi가 할당되지 않았다.
resources:
limits:
memory: 1Gi
requests:
memory: 1Gi
다음은 컨테이너가 하나인 파드의 예시 매니페스트이다. 해당 컨테이너는 메모리 요청량은 지정하지만, 상한은 지정하지 않는다.
apiVersion: v1
kind: Pod
metadata:
name: default-mem-demo-3
spec:
containers:
- name: default-mem-demo-3-ctr
image: nginx
resources:
requests:
memory: "128Mi"
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod-3.yaml --namespace=default-mem-example
파드 사양을 확인한다.
kubectl get pod default-mem-demo-3 --output=yaml --namespace=default-mem-example
출력을 보면 컨테이너의 매니페스트에 명시한 값대로 컨테이너의 메모리 요청량이 설정된 것을 알 수 있다. 해당 컨테이너의 메모리 상한은 512 MiB로 설정되며, 이는 네임스페이스의 메모리 상한 기본값과 일치한다.
resources:
limits:
memory: 512Mi
requests:
memory: 128Mi
리밋레인지
는 적용되는 기본 값의 일관성을 검사하지 않는다. 이는 리밋레인지
에 의해 설정된 _상한_의 기본값이 클라이언트가 API 서버에 제출하는 사양의 컨테이너에 대해 지정된 요청량 값보다 작을 수 있음을 의미한다. 이 경우, 최종 파드는 스케줄링할 수 없다.
자세한 내용은 리밋 레인지 개요를 참조한다.네임스페이스에 리소스 쿼터가 설정되어 있는 경우, 메모리 상한에 기본값을 설정하는 것이 좋다. 다음은 리소스 쿼터가 네임스페이스에 적용하는 세 가지 제한 사항이다.
리밋레인지를 추가할 때에는 다음을 고려해야 한다.
컨테이너를 갖고 있는 해당 네임스페이스의 파드가 자체 메모리 상한을 지정하지 않았다면, 컨트롤 플레인이 해당 컨테이너에 메모리 상한 기본값을 적용하며, 해당 파드는 메모리 리소스쿼터가 적용된 네임스페이스에서 실행되도록 허용될 수 있다.
네임스페이스를 삭제한다.
kubectl delete namespace default-mem-example
이 페이지는 네임스페이스에 대한 기본 CPU 요청량(request) 및 상한(limit)을 구성하는 방법을 보여준다.
쿠버네티스 클러스터를 여러 네임스페이스로 나눌 수 있다. 기본 CPU 상한이 설정되어 있는 네임스페이스에 파드를 생성했는데, 해당 파드의 모든 컨테이너에 CPU 상한이 명시되어 있지 않다면, 컨트롤 플레인이 해당 컨테이너에 기본 CPU 상한을 할당한다.
쿠버네티스는 기본 CPU 사용량을 할당하는데, 이는 이 페이지의 이후 부분에서 설명될 특정 조건 하에서만 수행된다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
클러스터에 네임스페이스를 생성할 수 있는 권한이 있어야 한다.
쿠버네티스에서 “1.0 CPU”가 무엇을 의미하는지 익숙하지 않다면, CPU의 의미를 참조한다.
이 연습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.
kubectl create namespace default-cpu-example
다음은 예시 리밋레인지에 대한 매니페스트이다. 이 매니페스트는 기본 CPU 요청량 및 기본 CPU 상한을 지정한다.
apiVersion: v1
kind: LimitRange
metadata:
name: cpu-limit-range
spec:
limits:
- default:
cpu: 1
defaultRequest:
cpu: 0.5
type: Container
default-cpu-example 네임스페이스에 리밋레인지를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults.yaml --namespace=default-cpu-example
이제 파드를 default-cpu-example
네임스페이스에 생성하고,
해당 파드의 어떤 컨테이너도 자체 CPU 요청량(request)과 상한(limit)을 명시하지 않으면,
컨트롤 플레인이 해당 컨테이너에 CPU 요청량의 기본값(0.5)과
상한의 기본값(1)을 지정한다.
다음은 컨테이너가 하나인 파드의 매니페스트이다. 해당 컨테이너는 CPU 요청량과 상한을 지정하지 않는다.
apiVersion: v1
kind: Pod
metadata:
name: default-cpu-demo
spec:
containers:
- name: default-cpu-demo-ctr
image: nginx
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults-pod.yaml --namespace=default-cpu-example
파드의 사양을 확인한다.
kubectl get pod default-cpu-demo --output=yaml --namespace=default-cpu-example
출력을 보면 파드 내 유일한 컨테이너의 CPU 요청량이 500m cpu
("500 밀리cpu"로 읽을 수 있음)이고,
CPU 상한이 1 cpu
임을 알 수 있다.
이것은 리밋레인지에 의해 지정된 기본값이다.
containers:
- image: nginx
imagePullPolicy: Always
name: default-cpu-demo-ctr
resources:
limits:
cpu: "1"
requests:
cpu: 500m
다음은 컨테이너가 하나인 파드의 매니페스트이다. 해당 컨테이너는 CPU 상한은 지정하지만, 요청량은 지정하지 않는다.
apiVersion: v1
kind: Pod
metadata:
name: default-cpu-demo-2
spec:
containers:
- name: default-cpu-demo-2-ctr
image: nginx
resources:
limits:
cpu: "1"
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults-pod-2.yaml --namespace=default-cpu-example
생성한 파드의 명세를 확인한다.
kubectl get pod default-cpu-demo-2 --output=yaml --namespace=default-cpu-example
출력 결과는 컨테이너의 CPU 요청량이 CPU 상한과 일치하도록 설정되었음을 보여준다.
참고로 컨테이너에는 CPU 요청량의 기본값인 0.5 cpu
가 할당되지 않았다.
resources:
limits:
cpu: "1"
requests:
cpu: "1"
다음은 컨테이너가 하나인 파드의 예시 매니페스트이다. 해당 컨테이너는 CPU 요청량은 지정하지만, 상한은 지정하지 않는다.
apiVersion: v1
kind: Pod
metadata:
name: default-cpu-demo-3
spec:
containers:
- name: default-cpu-demo-3-ctr
image: nginx
resources:
requests:
cpu: "0.75"
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults-pod-3.yaml --namespace=default-cpu-example
생성한 파드의 명세를 확인한다.
kubectl get pod default-cpu-demo-3 --output=yaml --namespace=default-cpu-example
출력을 보면 파드 생성 시 명시한 값대로
컨테이너의 CPU 요청량이 설정된 것을 알 수 있다(다시 말해, 매니페스트와 일치한다).
그러나, 해당 컨테이너의 CPU 상한은 1 cpu
로 설정되며,
이는 네임스페이스의 CPU 상한 기본값이다.
resources:
limits:
cpu: "1"
requests:
cpu: 750m
네임스페이스에 리소스 쿼터가 설정되어 있는 경우, CPU 상한에 대해 기본값을 설정하는 것이 좋다. 다음은 CPU 리소스 쿼터가 네임스페이스에 적용하는 두 가지 제한 사항이다.
리밋레인지를 추가할 때에는 다음을 고려해야 한다.
컨테이너를 갖고 있는 해당 네임스페이스의 파드가 자체 CPU 상한을 지정하지 않았다면, 컨트롤 플레인이 해당 컨테이너에 CPU 상한 기본값을 적용하며, 해당 파드는 CPU 리소스쿼터가 적용된 네임스페이스에서 실행되도록 허용될 수 있다.
네임스페이스를 삭제한다.
kubectl delete namespace default-cpu-example
이 페이지는 네임스페이스에서 실행되는 컨테이너가 사용하는 메모리의 최솟값과 최댓값을 설정하는 방법을 보여준다. 리밋레인지(LimitRange) 오브젝트에 최소 및 최대 메모리 값을 지정한다. 파드가 리밋레인지에 의해 부과된 제약 조건을 충족하지 않으면, 네임스페이스에서 생성될 수 없다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
클러스터에 네임스페이스를 생성할 수 있는 권한이 있어야 한다.
클러스터의 각 노드에는 파드가 사용할 수 있는 메모리가 최소 1GiB 이상 있어야 한다.
이 연습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.
kubectl create namespace constraints-mem-example
다음은 리밋레인지의 예시 매니페스트이다.
apiVersion: v1
kind: LimitRange
metadata:
name: mem-min-max-demo-lr
spec:
limits:
- max:
memory: 1Gi
min:
memory: 500Mi
type: Container
리밋레인지를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints.yaml --namespace=constraints-mem-example
리밋레인지에 대한 자세한 정보를 본다.
kubectl get limitrange mem-min-max-demo-lr --namespace=constraints-mem-example --output=yaml
출력 결과는 예상대로 메모리의 최소 및 최대 제약 조건을 보여준다. 그러나 참고로 리밋레인지의 구성 파일에 기본값(default)을 지정하지 않아도 자동으로 생성된다.
limits:
- default:
memory: 1Gi
defaultRequest:
memory: 1Gi
max:
memory: 1Gi
min:
memory: 500Mi
type: Container
이제 constraints-mem-example
네임스페이스에 파드를 생성할 때마다,
쿠버네티스는 다음 단계를 수행한다.
해당 파드의 어떤 컨테이너도 자체 메모리 요청량(request)과 상한(limit)을 명시하지 않으면, 컨트롤 플레인이 해당 컨테이너에 메모리 요청량과 상한의 기본값(default)을 지정한다.
해당 파드의 모든 컨테이너의 메모리 요청량이 최소 500 MiB 이상인지 확인한다.
해당 파드의 모든 컨테이너의 메모리 요청량이 1024 MiB(1 GiB)를 넘지 않는지 확인한다.
다음은 컨테이너가 하나인 파드의 매니페스트이다. 파드 명세 내에, 파드의 유일한 컨테이너는 600 MiB의 메모리 요청량 및 800 MiB의 메모리 상한을 지정하고 있다. 이는 리밋레인지에 의해 부과된 최소 및 최대 메모리 제약 조건을 충족시킨다.
apiVersion: v1
kind: Pod
metadata:
name: constraints-mem-demo
spec:
containers:
- name: constraints-mem-demo-ctr
image: nginx
resources:
limits:
memory: "800Mi"
requests:
memory: "600Mi"
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod.yaml --namespace=constraints-mem-example
파드가 실행 중이고 컨테이너의 상태가 정상인지 확인한다.
kubectl get pod constraints-mem-demo --namespace=constraints-mem-example
파드에 대한 자세한 정보를 본다.
kubectl get pod constraints-mem-demo --output=yaml --namespace=constraints-mem-example
출력을 보면 파드의 컨테이너의 메모리 요청량이 600 MiB이고 메모리 상한이 800 MiB임을 알 수 있다. 이는 리밋레인지에 의해 해당 네임스페이스에 부과된 제약 조건을 만족시킨다.
resources:
limits:
memory: 800Mi
requests:
memory: 600Mi
파드를 삭제한다.
kubectl delete pod constraints-mem-demo --namespace=constraints-mem-example
다음은 컨테이너가 하나인 파드의 매니페스트이다. 컨테이너는 800MiB의 메모리 요청량과 1.5GiB의 메모리 상한을 지정하고 있다.
apiVersion: v1
kind: Pod
metadata:
name: constraints-mem-demo-2
spec:
containers:
- name: constraints-mem-demo-2-ctr
image: nginx
resources:
limits:
memory: "1.5Gi"
requests:
memory: "800Mi"
파드 생성을 시도한다.
kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-2.yaml --namespace=constraints-mem-example
결과를 보면 파드가 생성되지 않은 것을 확인할 수 있으며, 이는 해당 파드가 정의하고 있는 컨테이너가 허용된 것보다 더 많은 메모리를 요청하고 있기 때문이다.
Error from server (Forbidden): error when creating "examples/admin/resource/memory-constraints-pod-2.yaml":
pods "constraints-mem-demo-2" is forbidden: maximum memory usage per Container is 1Gi, but limit is 1536Mi.
다음은 컨테이너가 하나인 파드의 매니페스트이다. 컨테이너는 100MiB의 메모리 요청량과 800MiB의 메모리 상한을 지정하고 있다.
apiVersion: v1
kind: Pod
metadata:
name: constraints-mem-demo-3
spec:
containers:
- name: constraints-mem-demo-3-ctr
image: nginx
resources:
limits:
memory: "800Mi"
requests:
memory: "100Mi"
파드 생성을 시도한다.
kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-3.yaml --namespace=constraints-mem-example
결과를 보면 파드가 생성되지 않은 것을 확인할 수 있으며, 이는 해당 파드가 정의하고 있는 컨테이너가 지정된 최저 메모리 요청량보다도 낮은 메모리 요청량을 지정하고 있기 때문이다.
Error from server (Forbidden): error when creating "examples/admin/resource/memory-constraints-pod-3.yaml":
pods "constraints-mem-demo-3" is forbidden: minimum memory usage per Container is 500Mi, but request is 100Mi.
다음은 컨테이너가 하나인 파드의 매니페스트이다. 해당 컨테이너는 메모리 요청량과 상한을 지정하지 않는다.
apiVersion: v1
kind: Pod
metadata:
name: constraints-mem-demo-4
spec:
containers:
- name: constraints-mem-demo-4-ctr
image: nginx
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-4.yaml --namespace=constraints-mem-example
파드에 대한 자세한 정보를 본다.
kubectl get pod constraints-mem-demo-4 --namespace=constraints-mem-example --output=yaml
출력을 보면 파드의 유일한 컨테이너에 대한 메모리 요청량이 1 GiB이고 메모리 상한도 1 GiB이다. 이 컨테이너는 어떻게 이런 값을 얻었을까?
resources:
limits:
memory: 1Gi
requests:
memory: 1Gi
파드가 해당 컨테이너에 대해 메모리 요청량과 상한을 지정하지 않았으므로, 클러스터가 리밋레인지로부터 메모리의 요청량과 상한 기본값을 적용하였다.
이는 곧 파드 정의에서 이 값들을 볼 수 있음을 의미한다.
kubectl describe
명령을 사용하여 확인할 수 있다.
# 출력에서 "Requests:" 섹션을 확인한다
kubectl describe pod constraints-mem-demo-4 --namespace=constraints-mem-example
이 시점에서, 파드는 실행 중일 수도 있고 아닐 수도 있다. 이 태스크의 전제 조건은 노드에 최소 1GiB의 메모리가 있어야 한다는 것이다. 각 노드에 1GiB의 메모리만 있는 경우, 노드에 할당할 수 있는 메모리가 1GiB의 메모리 요청량을 수용하기에 충분하지 않을 수 있다. 메모리가 2GiB인 노드를 사용하는 경우에는, 메모리가 1GiB 요청량을 수용하기에 충분할 것이다.
파드를 삭제한다.
kubectl delete pod constraints-mem-demo-4 --namespace=constraints-mem-example
리밋레인지에 의해 네임스페이스에 부과된 메모리의 최대 및 최소 제약 조건은 파드를 생성하거나 업데이트할 때만 적용된다. 리밋레인지를 변경해도, 이전에 생성된 파드에는 영향을 미치지 않는다.
클러스터 관리자는 파드가 사용할 수 있는 메모리 양에 제한을 둘 수 있다. 예를 들면 다음과 같다.
클러스터의 각 노드에는 2GiB의 메모리가 있다. 클러스터의 어떤 노드도 2GiB 이상의 요청량을 지원할 수 없으므로, 2GiB 이상의 메모리를 요청하는 파드를 수락하지 않으려고 한다.
클러스터는 운영 부서와 개발 부서에서 공유한다. 프로덕션 워크로드가 최대 8GiB의 메모리를 소비하도록 하려면, 개발 워크로드를 512MiB로 제한해야 한다. 프로덕션 및 개발을 위해 별도의 네임스페이스를 만들고, 각 네임스페이스에 메모리 제약 조건을 적용한다.
네임스페이스를 삭제한다.
kubectl delete namespace constraints-mem-example
이 페이지는 네임스페이스에서 컨테이너와 파드가 사용하는 CPU 리소스의 최솟값과 최댓값을 설정하는 방법을 보여준다. 리밋레인지(LimitRange) 오브젝트에 CPU의 최솟값과 최댓값을 지정한다. 리밋레인지에 의해 부과된 제약 조건을 파드가 충족하지 않으면, 해당 네임스페이스에 생성될 수 없다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
클러스터에 네임스페이스를 생성할 수 있는 권한이 있어야 한다.
클러스터의 각 노드는 파드 실행을 위해 적어도 1.0 CPU 이상이 사용 가능해야 한다. 쿠버네티스에서 “1 CPU”가 무엇을 의미하는지 알아보려면 CPU의 의미를 참조한다.
이 연습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.
kubectl create namespace constraints-cpu-example
다음은 리밋레인지 예제 매니페스트이다.
apiVersion: v1
kind: LimitRange
metadata:
name: cpu-min-max-demo-lr
spec:
limits:
- max:
cpu: "800m"
min:
cpu: "200m"
type: Container
리밋레인지를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints.yaml --namespace=constraints-cpu-example
리밋레인지에 대한 자세한 정보를 본다.
kubectl get limitrange cpu-min-max-demo-lr --output=yaml --namespace=constraints-cpu-example
출력 결과는 예상대로 CPU의 최소와 최대 제약 조건을 보여준다. 그러나 참고로 리밋레인지에 대한 구성 파일에 기본값을 지정하지 않아도 자동으로 생성된다.
limits:
- default:
cpu: 800m
defaultRequest:
cpu: 800m
max:
cpu: 800m
min:
cpu: 200m
type: Container
이제 constraints-cpu-example
네임스페이스에 파드를 생성할 때마다(또는
다른 쿠버네티스 API 클라이언트가 동일한 파드를 생성할 때마다), 쿠버네티스는 다음 단계를 수행한다.
해당 파드의 어떤 컨테이너도 자체 CPU 요청량(request)과 상한(limit)을 명시하지 않으면, 컨트롤 플레인이 해당 컨테이너에 CPU 요청량과 상한의 기본값(default)을 지정한다.
해당 파드의 모든 컨테이너가 200 millicpu 이상의 CPU 요청량을 지정하는지 확인한다.
해당 파드의 모든 컨테이너가 800 millicpu 이하의 CPU 상한을 지정하는지 확인한다.
LimitRange
오브젝트를 생성할 때, huge-pages
또는 GPU에도 상한을 지정할 수 있다. 그러나, 이 리소스들에 default
와 defaultRequest
가
모두 지정되어 있으면, 두 값은 같아야 한다.다음은 컨테이너가 하나인 파드의 매니페스트이다. 컨테이너 매니페스트는 500 millicpu의 CPU 요청량 및 800 millicpu의 CPU 상한을 지정하고 있다. 이는 이 네임스페이스의 리밋레인지에 의해 부과된 CPU의 최소와 최대 제약 조건을 충족시킨다.
apiVersion: v1
kind: Pod
metadata:
name: constraints-cpu-demo
spec:
containers:
- name: constraints-cpu-demo-ctr
image: nginx
resources:
limits:
cpu: "800m"
requests:
cpu: "500m"
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod.yaml --namespace=constraints-cpu-example
파드가 실행 중이고 컨테이너의 상태가 정상인지 확인한다.
kubectl get pod constraints-cpu-demo --namespace=constraints-cpu-example
파드에 대한 자세한 정보를 본다.
kubectl get pod constraints-cpu-demo --output=yaml --namespace=constraints-cpu-example
출력 결과는 파드 내 유일한 컨테이너의 CPU 요청량이 500 millicpu이고, CPU 상한이 800 millicpu임을 나타낸다. 이는 리밋레인지에 의해 부과된 제약 조건을 만족시킨다.
resources:
limits:
cpu: 800m
requests:
cpu: 500m
kubectl delete pod constraints-cpu-demo --namespace=constraints-cpu-example
다음은 컨테이너가 하나인 파드의 매니페스트이다. 컨테이너는 500 millicpu의 CPU 요청량과 1.5 cpu의 CPU 상한을 지정하고 있다.
apiVersion: v1
kind: Pod
metadata:
name: constraints-cpu-demo-2
spec:
containers:
- name: constraints-cpu-demo-2-ctr
image: nginx
resources:
limits:
cpu: "1.5"
requests:
cpu: "500m"
파드 생성을 시도한다.
kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod-2.yaml --namespace=constraints-cpu-example
결과를 보면 파드가 생성되지 않은 것을 확인할 수 있으며, 이는 해당 파드가 수용 불가능한 컨테이너를 정의하고 있기 때문이다. 해당 컨테이너가 수용 불가능한 이유는 너무 큰 CPU 상한을 지정하고 있기 때문이다.
Error from server (Forbidden): error when creating "examples/admin/resource/cpu-constraints-pod-2.yaml":
pods "constraints-cpu-demo-2" is forbidden: maximum cpu usage per Container is 800m, but limit is 1500m.
다음은 컨테이너가 하나인 파드의 매니페스트이다. 컨테이너는 100 millicpu의 CPU 요청량과 800 millicpu의 CPU 상한을 지정하고 있다.
apiVersion: v1
kind: Pod
metadata:
name: constraints-cpu-demo-3
spec:
containers:
- name: constraints-cpu-demo-3-ctr
image: nginx
resources:
limits:
cpu: "800m"
requests:
cpu: "100m"
파드 생성을 시도한다.
kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod-3.yaml --namespace=constraints-cpu-example
결과를 보면 파드가 생성되지 않은 것을 확인할 수 있으며, 이는 해당 파드가 수용 불가능한 컨테이너를 정의하고 있기 때문이다. 해당 컨테이너가 수용 불가능한 이유는 지정된 최저 CPU 요청량보다도 낮은 CPU 요청량을 지정하고 있기 때문이다.
Error from server (Forbidden): error when creating "examples/admin/resource/cpu-constraints-pod-3.yaml":
pods "constraints-cpu-demo-3" is forbidden: minimum cpu usage per Container is 200m, but request is 100m.
다음은 컨테이너가 하나인 파드의 매니페스트이다. 컨테이너는 CPU 요청량을 지정하지 않았으며, CPU 상한도 지정하지 않았다.
apiVersion: v1
kind: Pod
metadata:
name: constraints-cpu-demo-4
spec:
containers:
- name: constraints-cpu-demo-4-ctr
image: vish/stress
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod-4.yaml --namespace=constraints-cpu-example
파드에 대한 자세한 정보를 본다.
kubectl get pod constraints-cpu-demo-4 --namespace=constraints-cpu-example --output=yaml
출력을 보면 파드의 유일한 컨테이너에 대한 CPU 요청량이 800 millicpu이고, CPU 상한이 800 millicpu이다. 이 컨테이너는 어떻게 이런 값을 얻었을까?
resources:
limits:
cpu: 800m
requests:
cpu: 800m
컨테이너가 자체 CPU 요청량과 상한을 지정하지 않았으므로, 컨테이너가 이 네임스페이스에 대해 리밋레인지로부터 CPU 요청량과 상한의 기본값을 적용했다.
이 시점에서, 파드는 실행 중일 수도 있고 아닐 수도 있다. 이 태스크의 전제 조건은 노드에 1 CPU 이상 사용 가능해야 한다는 것이다. 각 노드에 1 CPU만 있는 경우, 노드에 할당할 수 있는 CPU가 800 millicpu의 요청량을 수용하기에 충분하지 않을 수 있다. 2 CPU인 노드를 사용하는 경우에는, CPU가 800 millicpu 요청량을 수용하기에 충분할 것이다.
파드를 삭제한다.
kubectl delete pod constraints-cpu-demo-4 --namespace=constraints-cpu-example
리밋레인지에 의해 네임스페이스에 부과된 CPU의 최대 및 최소 제약 조건은 파드를 생성하거나 업데이트할 때만 적용된다. 리밋레인지를 변경해도, 이전에 생성된 파드에는 영향을 미치지 않는다.
클러스터 관리자는 파드가 사용할 수 있는 CPU 리소스에 제한을 둘 수 있다. 예를 들면 다음과 같다.
클러스터의 각 노드에는 2 CPU가 있다. 클러스터의 어떤 노드도 요청량을 지원할 수 없기 때문에, 2 CPU 이상을 요청하는 파드를 수락하지 않으려고 한다.
클러스터는 프로덕션과 개발 부서에서 공유한다. 프로덕션 워크로드가 최대 3 CPU를 소비하도록 하고 싶지만, 개발 워크로드는 1 CPU로 제한하려고 한다. 프로덕션과 개발을 위해 별도의 네임스페이스를 생성하고, 각 네임스페이스에 CPU 제약 조건을 적용한다.
네임스페이스를 삭제한다.
kubectl delete namespace constraints-cpu-example
이 페이지는 네임스페이스에서 실행 중인 모든 파드가 사용할 수 있는 총 메모리 및 CPU 양에 대한 쿼터를 설정하는 방법을 보여준다. 리소스쿼터(ResourceQuota) 오브젝트에 쿼터를 지정할 수 있다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
클러스터에 네임스페이스를 생성할 수 있는 권한이 있어야 한다.
클러스터의 각 노드에는 최소 1GiB의 메모리가 있어야 한다.
이 연습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.
kubectl create namespace quota-mem-cpu-example
다음은 예시 리소스쿼터 오브젝트에 대한 매니페스트이다.
apiVersion: v1
kind: ResourceQuota
metadata:
name: mem-cpu-demo
spec:
hard:
requests.cpu: "1"
requests.memory: 1Gi
limits.cpu: "2"
limits.memory: 2Gi
리소스쿼터를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu.yaml --namespace=quota-mem-cpu-example
리소스쿼터에 대한 자세한 정보를 본다.
kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml
리소스쿼터는 이러한 요구 사항을 quota-mem-cpu-example 네임스페이스에 배치한다.
쿠버네티스에서 “1 CPU”가 무엇을 의미하는지 알아보려면 CPU의 의미를 참조한다.
다음은 예시 파드에 대한 매니페스트이다.
apiVersion: v1
kind: Pod
metadata:
name: quota-mem-cpu-demo
spec:
containers:
- name: quota-mem-cpu-demo-ctr
image: nginx
resources:
limits:
memory: "800Mi"
cpu: "800m"
requests:
memory: "600Mi"
cpu: "400m"
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu-pod.yaml --namespace=quota-mem-cpu-example
파드가 실행 중이고 파드의 (유일한) 컨테이너의 상태가 정상인지 확인한다.
kubectl get pod quota-mem-cpu-demo --namespace=quota-mem-cpu-example
다시 한 번, 리소스쿼터에 대한 자세한 정보를 본다.
kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml
출력 결과는 쿼터와 사용된 쿼터를 함께 보여준다. 파드의 메모리와 CPU 요청량 및 상한이 쿼터를 초과하지 않은 것을 볼 수 있다.
status:
hard:
limits.cpu: "2"
limits.memory: 2Gi
requests.cpu: "1"
requests.memory: 1Gi
used:
limits.cpu: 800m
limits.memory: 800Mi
requests.cpu: 400m
requests.memory: 600Mi
jq
도구가 설치되어 있으면, (JSONPath를 사용하여) used
값만을 질의 하고,
정돈된 상태로 출력할 수 있다. 예시는 다음과 같다.
kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example -o jsonpath='{ .status.used }' | jq .
다음은 두 번째 파드에 대한 매니페스트이다.
apiVersion: v1
kind: Pod
metadata:
name: quota-mem-cpu-demo-2
spec:
containers:
- name: quota-mem-cpu-demo-2-ctr
image: redis
resources:
limits:
memory: "1Gi"
cpu: "800m"
requests:
memory: "700Mi"
cpu: "400m"
매니페스트에서, 파드의 메모리 요청량이 700MiB임을 알 수 있다. 사용된 메모리 요청량과 이 새 메모리 요청량의 합계가 메모리 요청량 쿼터를 초과함에 유의한다(600 MiB + 700 MiB > 1 GiB).
파드 생성을 시도한다.
kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu-pod-2.yaml --namespace=quota-mem-cpu-example
두 번째 파드는 생성되지 않는다. 출력 결과는 두 번째 파드를 생성하면 메모리 요청량의 총 합계가 메모리 요청량 쿼터를 초과함을 보여준다.
Error from server (Forbidden): error when creating "examples/admin/resource/quota-mem-cpu-pod-2.yaml":
pods "quota-mem-cpu-demo-2" is forbidden: exceeded quota: mem-cpu-demo,
requested: requests.memory=700Mi,used: requests.memory=600Mi, limited: requests.memory=1Gi
이 연습에서 보았듯이, 리소스쿼터를 사용하여 네임스페이스에서 실행 중인 모든 파드에 대한 메모리 요청량의 총 합계를 제한할 수 있다. 메모리 상한, CPU 요청량 및 CPU 상한의 총 합계를 제한할 수도 있다.
네임스페이스 내의 총 자원을 관리하는 것 대신, 개별 파드 또는 파드 내의 컨테이너별로 제한하고 싶을 수도 있다. 이러한 종류의 제한을 걸려면, 리밋레인지(LimitRange)를 사용한다.
네임스페이스를 삭제한다.
kubectl delete namespace quota-mem-cpu-example
이 페이지는 네임스페이스에서 실행할 수 있는 총 파드 수에 대한 쿼터를 설정하는 방법을 보여준다. 리소스쿼터(ResourceQuota) 오브젝트에 쿼터를 지정할 수 있다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
클러스터에 네임스페이스를 생성할 수 있는 권한이 있어야 한다.
이 실습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.
kubectl create namespace quota-pod-example
다음은 예시 리소스쿼터 오브젝트에 대한 매니페스트이다.
apiVersion: v1
kind: ResourceQuota
metadata:
name: pod-demo
spec:
hard:
pods: "2"
리소스쿼터를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/quota-pod.yaml --namespace=quota-pod-example
리소스쿼터에 대한 자세한 정보를 본다.
kubectl get resourcequota pod-demo --namespace=quota-pod-example --output=yaml
출력 결과는 네임스페이스에 두 개의 파드 쿼터가 있고, 현재 파드가 없음을 보여준다. 즉, 쿼터 중 어느 것도 사용되지 않았다.
spec:
hard:
pods: "2"
status:
hard:
pods: "2"
used:
pods: "0"
다음은 디플로이먼트(Deployment)에 대한 예시 매니페스트이다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-quota-demo
spec:
selector:
matchLabels:
purpose: quota-demo
replicas: 3
template:
metadata:
labels:
purpose: quota-demo
spec:
containers:
- name: pod-quota-demo
image: nginx
매니페스트에서, replicas: 3
은 쿠버네티스가 모두 동일한 애플리케이션을 실행하는
세 개의 새로운 파드를 만들도록 지시한다.
디플로이먼트를 생성한다.
kubectl apply -f https://k8s.io/examples/admin/resource/quota-pod-deployment.yaml --namespace=quota-pod-example
디플로이먼트에 대한 자세한 정보를 본다.
kubectl get deployment pod-quota-demo --namespace=quota-pod-example --output=yaml
출력을 보면 디플로이먼트가 3개의 레플리카를 정의하고 있음에도, 앞서 설정한 쿼터로 인해 2개의 파드만 생성되었음을 보여준다.
spec:
...
replicas: 3
...
status:
availableReplicas: 2
...
lastUpdateTime: 2021-04-02T20:57:05Z
message: 'unable to create pods: pods "pod-quota-demo-1650323038-" is forbidden:
exceeded quota: pod-demo, requested: pods=1, used: pods=2, limited: pods=2'
이 예제에서는 총 파드 수를 제한하는 리소스쿼터를 정의하였다. 하지만, 다른 종류의 오브젝트의 총 수를 제한할 수도 있다. 예를 들어, 한 네임스페이스에 존재할 수 있는 크론잡의 총 수를 제한할 수 있다.
네임스페이스를 삭제한다.
kubectl delete namespace quota-pod-example
이 페이지는 쿠버네티스에서 캘리코(Calico) 클러스터를 생성하는 몇 가지 빠른 방법을 살펴본다.
클라우드나 지역 클러스터 중에 어디에 배포할지 결정한다.
사전요구사항: gcloud.
캘리코로 GKE 클러스터를 시작하려면, --enable-network-policy
플래그를 추가한다.
문법
gcloud container clusters create [클러스터_이름] --enable-network-policy
예시
gcloud container clusters create my-calico-cluster --enable-network-policy
배포를 확인하기 위해, 다음 커맨드를 이용하자.
kubectl get pods --namespace=kube-system
캘리코 파드는 calico
로 시작한다. 각각의 상태가 Running
임을 확인하자.
Kubeadm을 이용해서 15분 이내에 지역 단일 호스트 캘리코 클러스터를 생성하려면, 캘리코 빠른 시작을 참고한다.
클러스터가 동작하면, 쿠버네티스 네트워크 폴리시(NetworkPolicy)를 시도하기 위해 네트워크 폴리시 선언하기를 따라 할 수 있다.
이 페이지는 어떻게 네트워크 폴리시(NetworkPolicy)로 실리움(Cilium)를 사용하는지 살펴본다.
실리움의 배경에 대해서는 실리움 소개를 읽어보자.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
실리움에 쉽게 친숙해지기 위해 Minikube에 실리움을 기본적인 데몬셋으로 설치를 수행하는 실리움 쿠버네티스 시작하기 안내를 따라 해볼 수 있다.
Minikube를 시작하려면 최소 버전으로 >= v1.5.2 이 필요하고, 다음의 실행 파라미터로 실행한다.
minikube version
minikube version: v1.5.2
minikube start --network-plugin=cni
minikube의 경우 CLI 도구를 사용하여 실리움을 설치할 수 있다. 그러기 위해서, 먼저 다음과 같은 명령을 사용하여 최신 버전의 CLI를 다운로드 한다.
curl -LO https://github.com/cilium/cilium-cli/releases/latest/download/cilium-linux-amd64.tar.gz
이후 다음과 같은 명령을 사용하여 다운받은 파일을 /usr/local/bin
디렉토리에 압축을 푼다.
sudo tar xzvfC cilium-linux-amd64.tar.gz /usr/local/bin
rm cilium-linux-amd64.tar.gz
위의 명령을 실행한 후, 이제 다음 명령으로 실리움(Cilium)을 설치할 수 있다.
cilium install
그런 다음 실리움은 자동으로 클러스터를 구성을 감지하고 성공적인 설치를 위해 적절한 구성요소를 만들고 설치한다. 구성요소는 다음과 같다.
cilium-ca
의 인증 기관(CA) 및 Hubble (실리움의 관측 가능성 계층)에 대한 인증서.설치 후, cilium status
명령으로 실리움 디플로이먼트의 전체 상태를 볼 수 있다.
status
명령으로 예상 출력을 참조한다.
여기.
시작하기 안내서의 나머지 부분은 예제 애플리케이션을 이용하여 L3/L4(예, IP 주소 + 포트) 모두의 보안 정책뿐만 아니라 L7(예, HTTP)의 보안 정책을 적용하는 방법을 설명한다.
실리움을 실 서비스 용도의 배포에 관련한 자세한 방법은 실리움 쿠버네티스 설치 안내를 살펴본다. 이 문서는 자세한 요구사항, 방법과 실제 데몬셋 예시를 포함한다.
실리움으로 클러스터를 배포하면 파드가 kube-system
네임스페이스에 추가된다.
파드의 목록을 보려면 다음을 실행한다.
kubectl get pods --namespace=kube-system -l k8s-app=cilium
다음과 유사한 파드의 목록을 볼 것이다.
NAME READY STATUS RESTARTS AGE
cilium-kkdhz 1/1 Running 0 3m23s
...
cilium
파드는 클러스터 각 노드에서 실행되며, 리눅스 BPF를 사용해서
해당 노드의 파드에 대한 트래픽 네트워크 폴리시를 적용한다.
클러스터가 동작하면, 실리움으로 쿠버네티스 네트워크 폴리시를 시도하기 위해 네트워크 폴리시 선언하기를 따라 할 수 있다. 재미있게 즐기고, 질문이 있다면 실리움 슬랙 채널을 이용하여 연락한다.
이 페이지는 네트워크 폴리시(NetworkPolicy)로 큐브 라우터(Kube-router)를 사용하는 방법을 살펴본다.
운영 중인 쿠버네티스 클러스터가 필요하다. 클러스터가 없다면, Kops, Bootkube, Kubeadm 등을 이용해서 클러스터를 생성할 수 있다.
큐브 라우터 애드온은 갱신된 모든 네트워크 폴리시 및 파드에 대해 쿠버네티스 API 서버를 감시하고, 정책에 따라 트래픽을 허용하거나 차단하도록 iptables 규칙와 ipset을 구성하는 네트워크 폴리시 컨트롤러와 함께 제공된다. 큐브 라우터 애드온을 설치하는 큐브 라우터를 클러스터 인스톨러와 함께 사용하기 안내서를 따라해 봅니다.
큐브 라우터 애드온을 설치한 후에는, 쿠버네티스 네트워크 폴리시를 시도하기 위해 네트워크 폴리시 선언하기를 따라 할 수 있다.
이 페이지는 네트워크 폴리시(NetworkPolicy)로 로마나(Romana)를 사용하는 방법을 살펴본다.
kubeadm 시작하기의 1, 2, 3 단계를 완료하자.
Kubeadm을 위한 컨테이너화된 설치 안내서를 따른다.
네트워크 폴리시를 적용하기 위해 다음 중에 하나를 사용하자.
로마나를 설치한 후에는, 쿠버네티스 네트워크 폴리시를 시도하기 위해 네트워크 폴리시 선언하기를 따라 할 수 있다.
이 페이지는 네트워크 폴리시(NetworkPolicy)로 위브넷(Weave Net)를 사용하는 방법을 살펴본다.
쿠버네티스 클러스터가 필요하다. 맨 땅에서부터 시작하기를 위해서 kubeadm 시작하기 안내서를 따른다.
애드온을 통한 쿠버네티스 통합하기 가이드를 따른다.
쿠버네티스의 위브넷 애드온은 쿠버네티스의 모든 네임스페이스의
네크워크 정책 어노테이션을 자동으로 모니터링하며,
정책에 따라 트래픽을 허용하고 차단하는 iptables
규칙을 구성하는
네트워크 폴리시 컨트롤러와 함께 제공된다.
위브넷이 동작하는지 확인한다.
다음 커맨드를 입력한다.
kubectl get pods -n kube-system -o wide
출력은 다음과 유사하다.
NAME READY STATUS RESTARTS AGE IP NODE
weave-net-1t1qg 2/2 Running 0 9d 192.168.2.10 worknode3
weave-net-231d7 2/2 Running 1 7d 10.2.0.17 worknodegpu
weave-net-7nmwt 2/2 Running 3 9d 192.168.2.131 masternode
weave-net-pmw8w 2/2 Running 0 9d 192.168.2.216 worknode2
위브넷 파드를 가진 각 노드와 모든 파드는 Running
이고 2/2 READY
이다(2/2
는 각 파드가 weave
와 weave-npc
를 가지고 있음을 뜻한다).
위브넷 애드온을 설치하고 나서, 쿠버네티스 네트워크 폴리시를 시도하기 위해 네트워크 폴리시 선언하기를 따라 할 수 있다. 질문이 있으면 슬랙 #weave-community 이나 Weave 유저그룹에 연락한다.
클라이언트 인증서로 인증을 사용하는 경우 easyrsa
, openssl
또는 cfssl
을 통해 인증서를 수동으로 생성할 수 있다.
easyrsa 는 클러스터 인증서를 수동으로 생성할 수 있다.
easyrsa3
의 패치 버전을 다운로드하여 압축을 풀고, 초기화한다.
curl -LO https://dl.k8s.io/easy-rsa/easy-rsa.tar.gz
tar xzf easy-rsa.tar.gz
cd easy-rsa-master/easyrsa3
./easyrsa init-pki
새로운 인증 기관(CA)을 생성한다. --batch
는 자동 모드를 설정한다.
--req-cn
는 CA의 새 루트 인증서에 대한 일반 이름(Common Name (CN))을 지정한다.
./easyrsa --batch "--req-cn=${MASTER_IP}@`date +%s`" build-ca nopass
서버 인증서와 키를 생성한다.
--subject-alt-name
인자로 API 서버에 접근이 가능한 IP와 DNS
이름을 설정한다. MASTER_CLUSTER_IP
는 일반적으로 API 서버와
컨트롤러 관리자 컴포넌트에 대해 --service-cluster-ip-range
인자로
지정된 서비스 CIDR의 첫 번째 IP이다. --days
인자는 인증서가 만료되는
일 수를 설정하는 데 사용된다.
또한, 아래 샘플에서는 cluster.local
을 기본 DNS 도메인
이름으로 사용하고 있다고 가정한다.
./easyrsa --subject-alt-name="IP:${MASTER_IP},"\
"IP:${MASTER_CLUSTER_IP},"\
"DNS:kubernetes,"\
"DNS:kubernetes.default,"\
"DNS:kubernetes.default.svc,"\
"DNS:kubernetes.default.svc.cluster,"\
"DNS:kubernetes.default.svc.cluster.local" \
--days=10000 \
build-server-full server nopass
pki/ca.crt
, pki/issued/server.crt
그리고 pki/private/server.key
를 디렉터리에 복사한다.
API 서버를 시작하는 파라미터에 다음과 같이 추가한다.
--client-ca-file=/yourdirectory/ca.crt
--tls-cert-file=/yourdirectory/server.crt
--tls-private-key-file=/yourdirectory/server.key
openssl 은 클러스터 인증서를 수동으로 생성할 수 있다.
ca.key를 2048bit로 생성한다.
openssl genrsa -out ca.key 2048
ca.key에 따라 ca.crt를 생성한다(인증서 유효 기간을 사용하려면 -days
를 사용한다).
openssl req -x509 -new -nodes -key ca.key -subj "/CN=${MASTER_IP}" -days 10000 -out ca.crt
server.key를 2048bit로 생성한다.
openssl genrsa -out server.key 2048
인증서 서명 요청(Certificate Signing Request (CSR))을 생성하기 위한 설정 파일을 생성한다.
파일에 저장하기 전에 꺾쇠 괄호(예: <MASTER_IP>
)로
표시된 값을 실제 값으로 대체한다(예: csr.conf
).
MASTER_CLUSTER_IP
의 값은 이전 하위 섹션에서
설명한 대로 API 서버의 서비스 클러스터 IP이다.
또한, 아래 샘플에서는 cluster.local
을 기본 DNS 도메인
이름으로 사용하고 있다고 가정한다.
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[ dn ]
C = <국가(country)>
ST = <도(state)>
L = <시(city)>
O = <조직(organization)>
OU = <조직 단위(organization unit)>
CN = <MASTER_IP>
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.default.svc
DNS.4 = kubernetes.default.svc.cluster
DNS.5 = kubernetes.default.svc.cluster.local
IP.1 = <MASTER_IP>
IP.2 = <MASTER_CLUSTER_IP>
[ v3_ext ]
authorityKeyIdentifier=keyid,issuer:always
basicConstraints=CA:FALSE
keyUsage=keyEncipherment,dataEncipherment
extendedKeyUsage=serverAuth,clientAuth
subjectAltName=@alt_names
설정 파일을 기반으로 인증서 서명 요청을 생성한다.
openssl req -new -key server.key -out server.csr -config csr.conf
ca.key, ca.crt 그리고 server.csr을 사용해서 서버 인증서를 생성한다.
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 10000 \
-extensions v3_ext -extfile csr.conf -sha256
인증서 서명 요청을 확인한다.
openssl req -noout -text -in ./server.csr
인증서를 확인한다.
openssl x509 -noout -text -in ./server.crt
마지막으로, API 서버 시작 파라미터에 동일한 파라미터를 추가한다.
cfssl 은 인증서 생성을 위한 또 다른 도구이다.
아래에 표시된 대로 커맨드 라인 도구를 다운로드하여 압축을 풀고 준비한다.
사용 중인 하드웨어 아키텍처 및 cfssl 버전에 따라 샘플 명령을 조정해야 할 수도 있다.
curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl_1.5.0_linux_amd64 -o cfssl
chmod +x cfssl
curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssljson_1.5.0_linux_amd64 -o cfssljson
chmod +x cfssljson
curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl-certinfo_1.5.0_linux_amd64 -o cfssl-certinfo
chmod +x cfssl-certinfo
아티팩트(artifact)를 보유할 디렉터리를 생성하고 cfssl을 초기화한다.
mkdir cert
cd cert
../cfssl print-defaults config > config.json
../cfssl print-defaults csr > csr.json
CA 파일을 생성하기 위한 JSON 설정 파일을 ca-config.json
예시와 같이 생성한다.
{
"signing": {
"default": {
"expiry": "8760h"
},
"profiles": {
"kubernetes": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry": "8760h"
}
}
}
}
CA 인증서 서명 요청(CSR)을 위한 JSON 설정 파일을
ca-csr.json
예시와 같이 생성한다. 꺾쇠 괄호로 표시된
값을 사용하려는 실제 값으로 변경한다.
{
"CN": "kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names":[{
"C": "국가<country>",
"ST": "도<state>",
"L": "시<city>",
"O": "조직<organization>",
"OU": "조직 단위<organization unit>"
}]
}
CA 키(ca-key.pem
)와 인증서(ca.pem
)을 생성한다.
../cfssl gencert -initca ca-csr.json | ../cfssljson -bare ca
API 서버의 키와 인증서를 생성하기 위한 JSON 구성파일을
server-csr.json
예시와 같이 생성한다. 꺾쇠 괄호 안의 값을
사용하려는 실제 값으로 변경한다. MASTER_CLUSTER_IP
는
이전 하위 섹션에서 설명한 API 서버의 클러스터 IP이다.
아래 샘플은 기본 DNS 도메인 이름으로 cluster.local
을
사용한다고 가정한다.
{
"CN": "kubernetes",
"hosts": [
"127.0.0.1",
"<MASTER_IP>",
"<MASTER_CLUSTER_IP>",
"kubernetes",
"kubernetes.default",
"kubernetes.default.svc",
"kubernetes.default.svc.cluster",
"kubernetes.default.svc.cluster.local"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [{
"C": "<국가(country)>",
"ST": "<도(state)>",
"L": "<시(city)>",
"O": "<조직(organization)>",
"OU": "<조직 단위(organization unit)>"
}]
}
API 서버 키와 인증서를 생성하면, 기본적으로
server-key.pem
과 server.pem
파일에 각각 저장된다.
../cfssl gencert -ca=ca.pem -ca-key=ca-key.pem \
--config=ca-config.json -profile=kubernetes \
server-csr.json | ../cfssljson -bare server
클라이언트 노드는 자체 서명된 CA 인증서를 유효한 것으로 인식하지 않을 수 있다. 비-프로덕션 디플로이먼트 또는 회사 방화벽 뒤에서 실행되는 디플로이먼트의 경우, 자체 서명된 CA 인증서를 모든 클라이언트에 배포하고 유효한 인증서의 로컬 목록을 새로 고칠 수 있다.
각 클라이언트에서, 다음 작업을 수행한다.
sudo cp ca.crt /usr/local/share/ca-certificates/kubernetes.crt
sudo update-ca-certificates
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....
done.
certificates.k8s.io
API를 사용하여
클러스터에서 TLS 인증서 관리에
설명된 대로 인증에 사용할 x509 인증서를 프로비전 할 수 있다.
이 페이지는 쿠버네티스 API를 사용하여 클러스터에 접근하는 방법을 보여준다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
쿠버네티스 API에 처음 접근하는 경우, 쿠버네티스
커맨드 라인 도구인 kubectl
을 사용한다.
클러스터에 접근하려면, 클러스터 위치를 알고 접근할 수 있는 자격 증명이 있어야 한다. 일반적으로, 시작하기 가이드를 통해 작업하거나, 다른 사람이 클러스터를 설정하고 자격 증명과 위치를 제공할 때 자동으로 설정된다.
다음의 명령으로 kubectl이 알고 있는 위치와 자격 증명을 확인한다.
kubectl config view
많은 예제는 kubectl 사용에 대한 소개를 제공한다. 전체 문서는 kubectl 매뉴얼에 있다.
kubectl은 API 서버 찾기와 인증을 처리한다. curl
이나 wget
과 같은 http 클라이언트 또는 브라우저를 사용하여 REST API에
직접 접근하려는 경우, API 서버를 찾고 인증할 수 있는 여러 가지 방법이 있다.
Go 또는 Python 클라이언트 라이브러리를 사용하면 프록시 모드에서 kubectl에 접근할 수 있다.
다음 명령은 kubectl을 리버스 프록시로 작동하는 모드에서 실행한다. API 서버 찾기와 인증을 처리한다.
다음과 같이 실행한다.
kubectl proxy --port=8080 &
자세한 내용은 kubectl 프록시를 참고한다.
그런 다음 curl, wget 또는 브라우저를 사용하여 API를 탐색할 수 있다.
curl http://localhost:8080/api/
출력은 다음과 비슷하다.
{
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "10.0.1.149:443"
}
]
}
다음과 같이 인증 토큰을 API 서버에 직접 전달하여 kubectl 프록시 사용을 피할 수 있다.
grep/cut
방식을 사용한다.
# .KUBECONFIG에 여러 콘텍스트가 있을 수 있으므로, 가능한 모든 클러스터를 확인한다.
kubectl config view -o jsonpath='{"Cluster name\tServer\n"}{range .clusters[*]}{.name}{"\t"}{.cluster.server}{"\n"}{end}'
# 위의 출력에서 상호 작용하려는 클러스터의 이름을 선택한다.
export CLUSTER_NAME="some_server_name"
# 클러스터 이름을 참조하는 API 서버를 가리킨다.
APISERVER=$(kubectl config view -o jsonpath="{.clusters[?(@.name==\"$CLUSTER_NAME\")].cluster.server}")
# 기본 서비스 어카운트용 토큰을 보관할 시크릿을 생성한다.
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: default-token
annotations:
kubernetes.io/service-account.name: default
type: kubernetes.io/service-account-token
EOF
# 토큰 컨트롤러가 해당 시크릿에 토큰을 기록할 때까지 기다린다.
while ! kubectl describe secret default-token | grep -E '^token' >/dev/null; do
echo "waiting for token..." >&2
sleep 1
done
# 토큰 값을 얻는다
TOKEN=$(kubectl get secret default-token -o jsonpath='{.data.token}' | base64 --decode)
# TOKEN으로 API 탐색
curl -X GET $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure
출력은 다음과 비슷하다.
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "10.0.1.149:443"
}
]
}
위의 예는 --insecure
플래그를 사용한다. 이로 인해 MITM 공격이
발생할 수 있다. kubectl이 클러스터에 접근하면 저장된 루트 인증서와
클라이언트 인증서를 사용하여 서버에 접근한다. (~/.kube
디렉터리에
설치된다.) 클러스터 인증서는 일반적으로 자체 서명되므로,
http 클라이언트가 루트 인증서를 사용하도록 하려면 특별한 구성이
필요할 수 있다.
일부 클러스터에서, API 서버는 인증이 필요하지 않다. 로컬 호스트에서 제공되거나, 방화벽으로 보호될 수 있다. 이에 대한 표준은 없다. 쿠버네티스 API에 대한 접근 제어는 클러스터 관리자로서 이를 구성하는 방법에 대해 설명한다. 이러한 접근 방식은 향후 고 가용성 지원과 충돌할 수 있다.
쿠버네티스는 공식적으로 Go, Python, Java, dotnet, JavaScript 및 Haskell 용 클라이언트 라이브러리를 지원한다. 쿠버네티스 팀이 아닌 작성자가 제공하고 유지 관리하는 다른 클라이언트 라이브러리가 있다. 다른 언어에서 API에 접근하고 인증하는 방법에 대해서는 클라이언트 라이브러리를 참고한다.
go get k8s.io/client-go@kubernetes-<kubernetes-version-number>
어떤 버전이 지원되는지를 확인하려면 https://github.com/kubernetes/client-go/releases를 참고한다.import "k8s.io/client-go/kubernetes"
가 맞다.Go 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.
package main
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
// kubeconfig에서 현재 콘텍스트를 사용한다
// path-to-kubeconfig -- 예를 들어, /root/.kube/config
config, _ := clientcmd.BuildConfigFromFlags("", "<path-to-kubeconfig>")
// clientset을 생성한다
clientset, _ := kubernetes.NewForConfig(config)
// 파드를 나열하기 위해 API에 접근한다
pods, _ := clientset.CoreV1().Pods("").List(context.TODO(), v1.ListOptions{})
fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))
}
애플리케이션이 클러스터 내의 파드로 배치된 경우, 파드 내에서 API 접근을 참고한다.
Python 클라이언트를 사용하려면, 다음 명령을 실행한다. pip install kubernetes
추가 설치 옵션은 Python Client Library 페이지를 참고한다.
Python 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.
from kubernetes import client, config
config.load_kube_config()
v1=client.CoreV1Api()
print("Listing pods with their IPs:")
ret = v1.list_pod_for_all_namespaces(watch=False)
for i in ret.items:
print("%s\t%s\t%s" % (i.status.pod_ip, i.metadata.namespace, i.metadata.name))
Java 클라이언트를 설치하려면, 다음을 실행한다.
# java 라이브러리를 클론한다
git clone --recursive https://github.com/kubernetes-client/java
# 프로젝트 아티팩트, POM 등을 설치한다
cd java
mvn install
어떤 버전이 지원되는지를 확인하려면 https://github.com/kubernetes-client/java/releases를 참고한다.
Java 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.
package io.kubernetes.client.examples;
import io.kubernetes.client.ApiClient;
import io.kubernetes.client.ApiException;
import io.kubernetes.client.Configuration;
import io.kubernetes.client.apis.CoreV1Api;
import io.kubernetes.client.models.V1Pod;
import io.kubernetes.client.models.V1PodList;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.KubeConfig;
import java.io.FileReader;
import java.io.IOException;
/**
* 쿠버네티스 클러스터 외부의 애플리케이션에서 Java API를 사용하는 방법에 대한 간단한 예
*
* <p>이것을 실행하는 가장 쉬운 방법: mvn exec:java
* -Dexec.mainClass="io.kubernetes.client.examples.KubeConfigFileClientExample"
*
*/
public class KubeConfigFileClientExample {
public static void main(String[] args) throws IOException, ApiException {
// KubeConfig의 파일 경로
String kubeConfigPath = "~/.kube/config";
// 파일시스템에서 클러스터 외부 구성인 kubeconfig 로드
ApiClient client =
ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath))).build();
// 전역 디폴트 api-client를 위에서 정의한 클러스터 내 클라이언트로 설정
Configuration.setDefaultApiClient(client);
// CoreV1Api는 전역 구성에서 디폴트 api-client를 로드
CoreV1Api api = new CoreV1Api();
// CoreV1Api 클라이언트를 호출한다
V1PodList list = api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null);
System.out.println("Listing all pods: ");
for (V1Pod item : list.getItems()) {
System.out.println(item.getMetadata().getName());
}
}
}
dotnet 클라이언트를 사용하려면, 다음 명령을 실행한다. dotnet add package KubernetesClient --version 1.6.1
추가 설치 옵션은 dotnet Client Library 페이지를 참고한다. 어떤 버전이 지원되는지를 확인하려면 https://github.com/kubernetes-client/csharp/releases를 참고한다.
dotnet 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.
using System;
using k8s;
namespace simple
{
internal class PodList
{
private static void Main(string[] args)
{
var config = KubernetesClientConfiguration.BuildDefaultConfig();
IKubernetes client = new Kubernetes(config);
Console.WriteLine("Starting Request!");
var list = client.ListNamespacedPod("default");
foreach (var item in list.Items)
{
Console.WriteLine(item.Metadata.Name);
}
if (list.Items.Count == 0)
{
Console.WriteLine("Empty!");
}
}
}
}
JavaScript 클라이언트를 설치하려면, 다음 명령을 실행한다. npm install @kubernetes/client-node
어떤 버전이 지원되는지를 확인하려면 https://github.com/kubernetes-client/javascript/releases를 참고한다.
JavaScript 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.
const k8s = require('@kubernetes/client-node');
const kc = new k8s.KubeConfig();
kc.loadFromDefault();
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
k8sApi.listNamespacedPod('default').then((res) => {
console.log(res.body);
});
어떤 버전이 지원되는지를 확인하려면 https://github.com/kubernetes-client/haskell/releases를 참고한다.
Haskell 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.
exampleWithKubeConfig :: IO ()
exampleWithKubeConfig = do
oidcCache <- atomically $ newTVar $ Map.fromList []
(mgr, kcfg) <- mkKubeClientConfig oidcCache $ KubeConfigFile "/path/to/kubeconfig"
dispatchMime
mgr
kcfg
(CoreV1.listPodForAllNamespaces (Accept MimeJSON))
>>= print
이 페이지는 클러스터 안에서 사용자의 DNS 파드(Pod) 를 설정하고 DNS 변환(DNS resolution) 절차를 사용자 정의하는 방법을 설명한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
클러스터는 CoreDNS 애드온을 구동하고 있어야 한다.
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.12.
버전 확인을 위해서, 다음 커맨드를 실행 kubectl version
.
DNS는 애드온 관리자 인 클러스터 애드온을 사용하여 자동으로 시작되는 쿠버네티스 내장 서비스이다.
metadata.name
필드에 kube-dns
로 이름이 지정된다.
그 의도는 기존의 kube-dns
서비스 이름을 사용하여
클러스터 내부의 주소를 확인하는 워크로드에 대한 상호 운용성이 증가된다.
kube-dns
로 서비스 이름을 사용하면,
해당 DNS 공급자가 어떤 공통 이름으로 실행되고 있는지에 대한 구현 세부 정보를 추상화한다.CoreDNS를 디플로이먼트(Deployment)로 실행하고 있을 경우,
일반적으로 고정 IP 주소를 갖는 쿠버네티스 서비스로 노출된다.
Kubelet 은 --cluster-dns=<dns-service-ip>
플래그를 사용하여
DNS 확인자 정보를 각 컨테이너에 전달한다.
DNS 이름에도 도메인이 필요하다. 사용자는 kubelet 에 있는 --cluster-domain=<default-local-domain>
플래그를
통하여 로컬 도메인을 설정할 수 있다.
DNS 서버는 정방향 조회(A 및 AAAA 레코드), 포트 조회(SRV 레코드), 역방향 IP 주소 조회(PTR 레코드) 등을 지원한다. 더 자세한 내용은 서비스 및 파드용 DNS를 참고한다.
만약 파드의 dnsPolicy
가 default
로 지정되어 있는 경우,
파드는 자신이 실행되는 노드의 이름 변환(name resolution) 구성을 상속한다.
파드의 DNS 변환도 노드와 동일하게 작동해야 한다.
그 외에는 알려진 이슈를 참고한다.
만약 위와 같은 방식을 원하지 않거나, 파드를 위해 다른 DNS 설정이 필요한 경우,
사용자는 kubelet 의 --resolv-conf
플래그를 사용할 수 있다.
파드가 DNS를 상속받지 못하도록 하기 위해 이 플래그를 ""로 설정한다.
DNS 상속을 위해 /etc/resolv.conf
이외의 파일을 지정할 경우 유효한 파일 경로를 설정한다.
CoreDNS는 dns 명세를 준수하며 클러스터 DNS 역할을 할 수 있는, 범용적인 권한을 갖는 DNS 서버이다.
CoreDNS는 모듈형이자 플러그인이 가능한 DNS 서버이며, 각 플러그인들은 CoreDNS에 새로운 기능을 부가한다. CoreDNS 서버는 CoreDNS 구성 파일인 Corefile을 관리하여 구성할 수 있다. 클러스터 관리자는 CoreDNS Corefile에 대한 컨피그맵을 수정하여 해당 클러스터에 대한 DNS 서비스 검색 동작을 변경할 수 있다.
쿠버네티스에서 CoreDNS는 아래의 기본 Corefile 구성으로 설치된다.
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
Corefile의 구성은 CoreDNS의 아래 플러그인을 포함한다.
http://localhost:8080/health
에 기록된다. 이 확장 구문에서 lameduck
은 프로세스를
비정상 상태(unhealthy)로 만들고, 프로세스가 종료되기 전에 5초 동안 기다린다.ttl
을 사용하면 응답에 대한 사용자 정의 TTL 을 지정할 수 있으며, 기본값은 5초이다.
허용되는 최소 TTL은 0초이며, 최대값은 3600초이다.
레코드가 캐싱되지 않도록 할 경우, TTL을 0으로 설정한다.pods insecure
옵션은 kube-dns 와의 하위 호환성을 위해 제공된다.pods verified
옵션을 사용하여, 일치하는 IP의 동일 네임스페이스(Namespace)에 파드가 존재하는 경우에만
A 레코드를 반환하게 할 수 있다.pods disabled
옵션은 파드 레코드를 사용하지 않을 경우 사용된다.http://localhost:9153/metrics
에서 사용 가능하다.사용자는 컨피그맵을 변경하여 기본 CoreDNS 동작을 변경할 수 있다.
CoreDNS는 포워드 플러그인을 사용하여 스텁 도메인 및 업스트림 네임서버를 구성할 수 있다.
만약 클러스터 운영자가 10.150.0.1 에 위치한 Consul 도메인 서버를 가지고 있고, 모든 Consul 이름의 접미사가 .consul.local 인 경우, CoreDNS에서 이를 구성하기 위해 클러스터 관리자는 CoreDNS 컨피그맵에서 다음 구문을 생성한다.
consul.local:53 {
errors
cache 30
forward . 10.150.0.1
}
모든 비 클러스터의 DNS 조회가 172.16.0.1 의 특정 네임서버를 통과하도록 할 경우,
/etc/resolv.conf
대신 forward
를 네임서버로 지정한다.
forward . 172.16.0.1
기본 Corefile
구성에 따른 최종 컨피그맵은 다음과 같다.
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . 172.16.0.1
cache 30
loop
reload
loadbalance
}
consul.local:53 {
errors
cache 30
forward . 10.150.0.1
}
Kubernetes v1.26 [stable]
쿠버네티스 v1.20부터 kubelet은 exec 플러그인을 사용하여 컨테이너 이미지 레지스트리에 대한 자격 증명(credential)을 동적으로 검색할 수 있다. kubelet과 exec 플러그인은 쿠버네티스 버전 API를 사용하여 stdio(stdin, stdout, stderr)를 통해 통신한다. kubelet은 플러그인을 통해 정적 자격 증명을 디스크에 저장하는 대신 컨테이너 레지스트리에 대한 자격 증명을 동적으로 요청할 수 있다. 예를 들어, 플러그인은 로컬 메타데이터 서버와 통신하여 kubelet에 의해 풀(pulled) 된 이미지에 대한 단기 유효(short-lived) 자격 증명을 검색할 수 있다.
아래 중 하나에 해당하면 이 기능의 사용을 고려해도 좋다.
이 가이드는 kubelet의 이미지 자격 증명 공급자 플러그인 메커니즘을 구성하는 방법을 보여 준다.
kubectl version
.
자격 증명 공급자 플러그인은 kubelet에 의해 실행될 실행 가능한 바이너리이다. 클러스터의 모든 노드에 플러그인 바이너리가 있고 알려진 디렉터리에 저장됐는지 확인한다. 이후 kubelet 플래그를 구성할 때 해당 디렉터리가 필요하다.
이 기능을 사용하려면 kubelet에 두 개의 플래그가 설정돼야 한다.
--image-credential-provider-config
- 자격 증명 공급자 플러그인 구성 파일 경로.--image-credential-provider-bin-dir
- 자격 증명 공급자 플러그인 바이너리 파일이 있는 디렉터리 경로.kubelet은 --image-credential-provider-config
로 전달된 구성 파일을 읽고,
컨테이너 이미지에 대해 어떤 exec 플러그인을 호출할지 결정한다.
ECR-based 플러그인을 사용하는 경우 사용하게 될 수 있는 구성 파일의 예:
apiVersion: kubelet.config.k8s.io/v1
kind: CredentialProviderConfig
# providers 필드는 kubelet이 활성화할 자격 증명 공급자 헬퍼 플러그인의 목록을 나타낸다.
# 단일 이미지에 대해 복수 공급자가 매치될 수도 있으며,
# 이러한 경우 모든 공급자의 자격 증명이 kubelet으로 리턴된다.
# 단일 이미지에 대해 복수 공급자가 호출된 경우, 결과가 합산된다.
# 공급자가 중복되는(overlapping) 인증 키를 리턴한 경우, 이 목록의 위쪽에 위치하는 공급자로부터의 값이 사용된다.
providers:
# name 필드는 자격 증명 공급자를 구분하기 위한 필수 필드이다.
# 이 이름은 kubelet이 인식하는 공급자 실행 파일의 이름과 일치해야 한다.
# 해당 실행 파일은 kubelet의 bin 디렉토리에 존재해야 한다(--image-credential-provider-bin-dir 플래그로 설정).
- name: ecr
# matchImages 필드는 각 이미지에 대해 이 공급자가 활성화되어야 하는지를
# 판단하기 위한 문자열의 목록을 나타내는 필수 필드이다.
# kubelet이 요청한 이미지가 다음 문자열 중 하나와 매치되면,
# 해당 플러그인이 활성화되어 자격 증명을 제공할 수 있게 된다.
# 이미지 태그 문자열은 저장소(registry) 도메인 및 URL 경로를 포함해야 한다.
#
# matchImages의 각 항목은 패턴을 나타내며, 포트와 경로를 포함할 수 있다.
# 도메인 자리에 글롭(glob)도 사용할 수 있으나, 포트와 경로에는 사용할 수 없다.
# 글롭은 '*.k8s.io' 또는 'k8s.*.io'와 같이 서브도메인 형태로 사용하거나, 'k8s.*'와 같이 최상위 도메인 형태로 사용할 수 있다.
# 'app*.k8s.io'와 같이 서브도메인의 일부를 매칭하는 것도 지원된다.
# 각 글롭은 단일 서브도메인 분할만을 매칭할 수 있으므로, `*.io`는 `*.k8s.io`에 매치되지 **않는다**.
#
# 다음 사항이 모두 만족될 때에만 image와 matchImage가 매치되었다고 판단한다.
# - 양쪽의 도메인 파트 수가 동일하고, 각 파트가 매치됨
# - imageMatch의 URL 경로가 타겟 이미지 URL 경로의 접두사임
# - imageMatch가 포트를 포함하면, 이미지 쪽에 기재된 포트와 매치됨
#
# matchImages 예시는 다음과 같다.
# - 123456789.dkr.ecr.us-east-1.amazonaws.com
# - *.azurecr.io
# - gcr.io
# - *.*.registry.io
# - registry.io:8080/path
matchImages:
- "*.dkr.ecr.*.amazonaws.com"
- "*.dkr.ecr.*.amazonaws.cn"
- "*.dkr.ecr-fips.*.amazonaws.com"
- "*.dkr.ecr.us-iso-east-1.c2s.ic.gov"
- "*.dkr.ecr.us-isob-east-1.sc2s.sgov.gov"
# defaultCacheDuration 필드는 캐시 기간이 플러그인 응답에 명시되지 않은 경우에
# 해당 플러그인이 자격 증명을 인메모리 캐시에 보관할 기본 기간을 지정한다. 이 필드는 필수이다.
defaultCacheDuration: "12h"
# apiVersion 필드는 CredentialProviderRequest를 실행할 때 기재될 입력 버전을 지정하는 필수 필드이다.
# 응답 CredentialProviderResponse는 입력과 동일한 인코딩 버전을 사용해야 한다. 현재 지원되는 값은 다음과 같다.
# - credentialprovider.kubelet.k8s.io/v1
apiVersion: credentialprovider.kubelet.k8s.io/v1
# args 필드는 커맨드를 실행할 때 전달할 인자를 지정하는 필드이다.
# 이 필드는 선택사항이다.
args:
- get-credentials
# env 필드는 프로세스에 노출할 추가적인 환경 변수를 기재하는 필드이다.
# 이들은 호스트의 환경 변수 및
# client-go가 플러그인에 인자를 전달하기 위해 사용하는 변수와 합산된다.
# 이 필드는 선택사항이다.
env:
- name: AWS_PROFILE
value: example_profile
providers
필드는 kubelet에서 사용되는 활성화된 플러그인의 목록으로, 각 항목에는 몇 가지 필수 필드가 있다.
name
: --image-credential-provider-bin-dir
로 전달된 디렉터리에 존재하는
실행 가능한 바이너리의 이름과 반드시 일치해야 하는 플러그인의 이름.matchImages
: 이 공급자를 호출할지 결정하기 위해 이미지를 대조하는 데 사용되는 문자열 목록.
아래의 더 많은 내용 참조.defaultCacheDuration
: 플러그인에 의해 캐시 기간이 지정되지 않으면,
kubelet이 메모리에 자격 증명을 캐시하는 기본 기간.apiVersion
: kubelet과 exec 플러그인이 통신할 때 사용하는 API 버전.각 자격 증명 공급자에게 인수(arg) 및 환경 변수도 선택적으로 제공할 수 있다. 플러그인에 필요한 인수 및 환경 변수 집합을 확인하려면 해당 플러그인 구현자에게 문의하는 것이 좋다.
kubelet은 각 자격 증명 공급자에 대한 matchImages
필드를 사용해 파드가 사용하고 있는 특정 이미지에 대해 플러그인을 호출할지 결정한다.
Globs는 도메인에서 사용할 수 있지만 포트나 경로에서는 사용할 수 없다.
Glob은 *.k8s.io
이나 k8s.*.io
같은 서브도메인과 k8s.*
와 같은 최상위 도메인으로 지원된다.
또한, app*.k8s.io
와 같은 부분 서브도메인을 매칭하는 것도 지원된다.
각 Glob은 단일 하위 도메인 세그먼트만 일치할 수 있으므로 *.io
는 *.k8s.io
과 일치하지 않는다.
아래와 같은 경우 이미지 이름과 matchImage
항목 사이에 매치가 존재한다.
matchImages
패턴의 예시 값은 아래와 같다.
123456789.dkr.ecr.us-east-1.amazonaws.com
*.azurecr.io
gcr.io
*.*.registry.io
foo.registry.io:8080/path
CredentialProviderConfig
에 대한 세부 정보 읽기커맨드 라인 플래그 대신 디스크 상의 구성 파일을 통해 Kubelet의 구성 파라미터 하위 집합을 설정할 수 있다.
구성 파일을 통해 파라미터를 제공하는 것은 노드 배포 및 구성 관리를 간소화하기 때문에 권장되는 접근 방식이다.
파일을 통해 구성할 수 있는
Kubelet 구성의 하위 집합은
KubeletConfiguration
구조체에 의해 정의된다.
구성 파일은 이 구조체의 파라미터를 반드시 JSON 또는 YAML로 표현한 파일이어야 한다. Kubelet이 파일에 읽기 권한이 있는지 확인한다.
다음은 이러한 파일에 대한 예시를 보여준다.
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: "192.168.0.8",
port: 20250,
serializeImagePulls: false,
evictionHard:
memory.available: "200Mi"
이 예제에서, Kubelet은 192.168.0.8 IP 주소와 20250 포트에서 동작하고, 이미지를 병렬로 가져오고, 사용 가능 메모리가 200Mi 아래로 떨어지면 파드를 축출하도록 구성되어 있다. 플래그에 의해 재정의(overridden)되지 않는한, 다른 모든 Kubelet 구성은 기본 제공 기본값으로 유지된다. 구성 파일과 동일한 값을 대상으로 하는 커맨드 라인 플래그는 해당 값을 재정의 한다.
kubeadmin init
으로 클러스터를 생성하는 동안 kubelet-config를 사용해야 한다.
자세한 내용은 kubeadm을 사용하여 kubelet 구성하기를 참고한다.Kubelet의 구성 파일 경로로 설정된 --config
플래그를 사용하여 Kubelet을 시작하면
Kubelet이 이 파일에서 구성을 불러온다.
구성 파일과 동일한 값을 대상으로 하는 커맨드 라인 플래그는 해당 값을 재정의한다는 점을 유의한다. 이렇게 하면 커맨드 라인 API와의 이전 버전과의 호환성을 보장할 수 있다.
Kubelet 구성 파일의 상대 파일 경로는 Kubelet 구성 파일의 위치를 기준으로 확인되는 반면, 커맨드 라인 플래그의 상대 경로는 Kubelet의 현재 작업 디렉터리를 기준으로 확인된다는 점에 유의한다.
일부 기본값은 커맨드 라인 플래그와 Kubelet 구성 파일 간에 다르다는 점에 유의한다.
--config
가 제공되고 명령줄을 통해 값을 지정하지 않은 경우,
KubeletConfiguration
버전의 기본값이 적용된다.
위 예제의 버전은 kubelet.config.k8s.io/v1beta1
이다.
KubeletConfiguration
를 참고한다.이 페이지는 특별한 요구사항이 없는 퍼시스턴트볼륨클레임(PersistentVolumeClaim)의 볼륨을 프로비저닝 하는데 사용되는 기본 스토리지 클래스를 변경하는 방법을 보여준다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
설치 방법에 따라, 사용자의 쿠버네티스 클러스터는 기본으로 표시된 기존 스토리지클래스와 함께 배포될 수 있다. 이 기본 스토리지클래스는 특정 스토리지 클래스가 필요하지 않은 퍼시스턴트볼륨클레임에 대해 스토리지를 동적으로 프로비저닝 하기 위해 사용된다. 더 자세한 내용은 퍼시스턴트볼륨클레임 문서를 보자.
미리 설치된 기본 스토리지클래스가 사용자의 예상되는 워크로드에 적합하지 않을수도 있다. 예를 들어, 너무 가격이 높은 스토리지를 프로비저닝 해야할 수도 있다. 이런 경우에, 기본 스토리지 클래스를 변경하거나 완전히 비활성화 하여 스토리지의 동적 프로비저닝을 방지할 수 있다.
기본 스토리지클래스를 삭제하는 경우, 사용자의 클러스터에서 구동 중인 애드온 매니저에 의해 자동으로 다시 생성될 수 있으므로 정상적으로 삭제가 되지 않을 수도 있다. 애드온 관리자 및 개별 애드온을 비활성화 하는 방법에 대한 자세한 내용은 설치 문서를 참조하자.
사용자의 클러스터에 있는 스토리지클래스 목록을 조회한다.
kubectl get storageclass
결과는 아래와 유사하다.
NAME PROVISIONER AGE
standard (default) kubernetes.io/gce-pd 1d
gold kubernetes.io/gce-pd 1d
기본 스토리지클래스는 (default)
로 표시되어 있다.
기본 스토리지클래스를 기본값이 아닌 것으로 표시한다.
기본 스토리지클래스에는
storageclass.kubernetes.io/is-default-class
의 값이 true
로 설정되어 있다.
다른 값이거나 어노테이션이 없을 경우 false
로 처리된다.
스토리지클래스를 기본값이 아닌 것으로 표시하려면, 그 값을 false
로 변경해야 한다.
kubectl patch storageclass standard -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
여기서 standard
는 사용자가 선택한 스토리지클래스의 이름이다.
스토리지클래스를 기본값으로 표시한다.
이전 과정과 유사하게, 어노테이션을 추가/설정해야 한다.
storageclass.kubernetes.io/is-default-class=true
.
kubectl patch storageclass gold -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
최대 1개의 스토리지클래스를 기본값으로 표시할 수 있다는 것을 알아두자. 만약
2개 이상이 기본값으로 표시되면, 명시적으로 storageClassName
가 지정되지 않은 PersistentVolumeClaim
은 생성될 수 없다.
사용자가 선택한 스토리지클래스가 기본값으로 되어 있는지 확인한다.
kubectl get storageclass
결과는 아래와 유사하다.
NAME PROVISIONER AGE
standard kubernetes.io/gce-pd 1d
gold (default) kubernetes.io/gce-pd 1d
이 페이지는 네임스페이스를 살펴보고, 작업하고, 삭제하는 방법에 대해 다룬다. 또한 쿠버네티스 네임스페이스를 사용해 클러스터를 세분화하는 방법에 대해서도 다룬다.
kubectl get namespaces
NAME STATUS AGE
default Active 11d
kube-system Active 11d
kube-public Active 11d
쿠버네티스를 시작하면 세 개의 초기 네임스페이스가 있다.
default
다른 네임스페이스가 없는 오브젝트를 위한 기본 네임스페이스kube-system
쿠버네티스 시스템에서 생성된 오브젝트의 네임스페이스kube-public
이 네임스페이스는 자동으로 생성되며 모든 사용자(미인증 사용자를 포함)가 읽을 수 있다. 이 네임스페이스는 일부 리소스를 공개적으로 보고 읽을 수 있어야 하는 경우에 대비하여 대부분이 클러스터 사용을 위해 예약돼 있다. 그러나 이 네임스페이스의 공개적인 성격은 관례일 뿐 요구 사항은 아니다.아래 명령을 실행해 특정 네임스페이스에 대한 요약 정보를 볼 수 있다.
kubectl get namespaces <name>
자세한 정보를 보는 것도 가능하다.
kubectl describe namespaces <name>
Name: default
Labels: <none>
Annotations: <none>
Status: Active
No resource quota.
Resource Limits
Type Resource Min Max Default
---- -------- --- --- ---
Container cpu - - 100m
이러한 세부 정보에는 리소스 한도(limit) 범위 뿐만 아니라 리소스 쿼터(만약 있다면)까지 모두 표시된다.
리소스 쿼터는 네임스페이스 내 리소스의 집계 사용량을 추적하며, 네임스페이스에서 사용할 수 있는 하드(Hard) 리소스 사용 제한을 클러스터 운영자가 정의할 수 있도록 해준다.
제한 범위는 하나의 엔티티(entity)가 하나의 네임스페이스에서 사용할 수 있는 리소스 양에 대한 최대/최소 제약 조건을 정의한다.
어드미션 컨트롤: 리밋 레인지(Limit Range)를 참조하자.
네임스페이스는 다음 두 상태 중 하나에 있을 수 있다.
Active
네임스페이스가 사용 중이다.Terminating
네임스페이스가 삭제 중이므로 새 오브젝트에 사용할 수 없다.자세한 내용은 API 레퍼런스의 네임스페이스를 참조한다.
`kube-` 접두사는 쿠버네티스 시스템 네임스페이스로 예약돼 있으므로 이를 사용해 네임스페이스를 생성하지 않도록 한다.
my-namespace.yaml
이라는 YAML 파일을 생성하고 아래 내용을 작성한다.
apiVersion: v1
kind: Namespace
metadata:
name: <insert-namespace-name-here>
다음 명령을 실행한다.
kubectl create -f ./my-namespace.yaml
아래 명령으로 네임스페이스를 생성할 수도 있다.
kubectl create namespace <insert-namespace-name-here>
네임스페이스의 이름은 유효한 DNS 레이블이어야 한다.
옵션 필드인 finalizer
는 네임스페이스가 삭제 될 때 관찰자가 리소스를 제거할 수 있도록 한다. 존재하지 않는 파이널라이저(finalizer)를 명시한 경우 네임스페이스는 생성되지만 사용자가 삭제하려 하면 Terminating
상태가 된다.
파이널라이저에 대한 자세한 내용은 네임스페이스 디자인 문서에서 확인할 수 있다.
다음 명령을 실행해 네임스페이스를 삭제한다.
kubectl delete namespaces <insert-some-namespace-name>
삭제는 비동기적이므로 삭제 후 한동안은 네임스페이스의 상태가 Terminating
으로 보일 것이다.
기본 네임스페이스 이해하기
기본적으로 쿠버네티스 클러스터는 클러스터에서 사용할 기본 파드, 서비스, 그리고 디플로이먼트(Deployment) 집합을 가지도록 클러스터를 프로비저닝 할 때 기본 네임스페이스를 인스턴스화한다.
새 클러스터가 있다고 가정하고 아래 명령을 수행하면 사용 가능한 네임스페이스를 볼 수 있다.
kubectl get namespaces
NAME STATUS AGE
default Active 13m
새 네임스페이스 생성하기
이 예제에서는 내용을 저장할 쿠버네티스 네임스페이스를 추가로 두 개 생성할 것이다.
개발과 프로덕션 유스케이스에서 공유 쿠버네티스 클러스터를 사용하는 조직이 있다고 가정하자.
개발 팀은 애플리케이션을 구축하고 실행하는데 사용하는 파드, 서비스, 디플로이먼트의 목록을 볼 수 있는 공간을 클러스터에 유지하려 한다. 이 공간에서는 쿠버네티스 리소스가 자유롭게 추가 및 제거되고, 누가 리소스를 수정할 수 있는지 없는지에 대한 제약이 완화돼 빠른 개발이 가능해진다.
운영 팀은 운영 사이트를 실행하는 파드, 서비스, 디플로이먼트 집합을 조작할 수 있는 사람과 그렇지 않은 사람들에 대해 엄격한 절차를 적용할 수 있는 공간을 클러스터에 유지하려 한다.
이 조직이 따를 수 있는 한 가지 패턴은 쿠버네티스 클러스터를 development(개발)
와 production(운영)
이라는 두 개의 네임스페이스로 분할하는 것이다.
우리의 작업을 보존하기 위해 새로운 네임스페이스 두 개를 만들자.
kubectl을 사용해 development
네임스페이스를 생성한다.
kubectl create -f https://k8s.io/examples/admin/namespace-dev.json
그런 다음 kubectl을 사용해 production
네임스페이스를 생성한다.
kubectl create -f https://k8s.io/examples/admin/namespace-prod.json
제대로 생성이 되었는지 확인하기 위해 클러스터 내의 모든 네임스페이스를 나열한다.
kubectl get namespaces --show-labels
NAME STATUS AGE LABELS
default Active 32m <none>
development Active 29s name=development
production Active 23s name=production
네임스페이스마다 파드 생성
쿠버네티스 네임스페이스는 클러스터의 파드, 서비스 그리고 디플로이먼트의 범위를 제공한다.
하나의 네임스페이스와 상호 작용하는 사용자는 다른 네임스페이스의 내용을 볼 수 없다.
이를 보여주기 위해 development
네임스페이스에 간단한 디플로이먼트와 파드를 생성하자.
kubectl create deployment snowflake --image=registry.k8s.io/serve_hostname -n=development --replicas=2
단순히 호스트명을 제공해주는 snowflake
라는 파드의 개수를 2개로 유지하는 디플로이먼트를 생성하였다.
kubectl get deployment -n=development
NAME READY UP-TO-DATE AVAILABLE AGE
snowflake 2/2 2 2 2m
kubectl get pods -l app=snowflake -n=development
NAME READY STATUS RESTARTS AGE
snowflake-3968820950-9dgr8 1/1 Running 0 2m
snowflake-3968820950-vgc4n 1/1 Running 0 2m
개발자들은 production
네임스페이스의 내용에 영향을 끼칠 걱정 없이 하고 싶은 것을 할 수 있으니 대단하지 않은가.
이제 production
네임스페이스로 전환해 한 네임스페이스의 리소스가 다른 네임스페이스에서는 어떻게 숨겨지는지 보자.
production
네임스페이스는 비어있어야 하며 아래 명령은 아무 것도 반환하지 않아야 한다.
kubectl get deployment -n=production
kubectl get pods -n=production
프로덕션이 가축 키우는 것을 좋아하듯이, 우리도 production
네임스페이스에 cattle(가축)이라는 이름의 파드를 생성한다.
kubectl create deployment cattle --image=registry.k8s.io/serve_hostname -n=production
kubectl scale deployment cattle --replicas=5 -n=production
kubectl get deployment -n=production
NAME READY UP-TO-DATE AVAILABLE AGE
cattle 5/5 5 5 10s
kubectl get pods -l app=cattle -n=production
NAME READY STATUS RESTARTS AGE
cattle-2263376956-41xy6 1/1 Running 0 34s
cattle-2263376956-kw466 1/1 Running 0 34s
cattle-2263376956-n4v97 1/1 Running 0 34s
cattle-2263376956-p5p3i 1/1 Running 0 34s
cattle-2263376956-sxpth 1/1 Running 0 34s
지금 쯤이면 사용자가 한 네임스페이스에 생성한 리소스는 다른 네임스페이스에서 숨겨져 있어야 한다는 것을 잘 알고 있을 것이다.
쿠버네티스 정책 지원이 발전함에 따라, 이 시나리오를 확장해 각 네임스페이스에 서로 다른 인증 규칙을 제공하는 방법을 보이도록 하겠다.
단일 클러스터는 여러 사용자 및 사용자 그룹(이하 '사용자 커뮤니티')의 요구를 충족시킬 수 있어야 한다.
쿠버네티스 네임스페이스 는 여러 프로젝트, 팀 또는 고객이 쿠버네티스 클러스터를 공유할 수 있도록 지원한다.
이를 위해 다음을 제공한다.
여러 개의 네임스페이스를 사용하는 것은 선택 사항이다.
각 사용자 커뮤니티는 다른 커뮤니티와 격리된 상태로 작업할 수 있기를 원한다.
각 사용자 커뮤니티는 다음을 가진다.
클러스터 운영자는 각 사용자 커뮤니티 마다 네임스페이스를 생성할 수 있다.
네임스페이스는 다음을 위한 고유한 범위를 제공한다.
유스케이스는 다음을 포함한다.
서비스를 생성하면 상응하는 DNS 엔트리(entry)가 생성된다.
이 엔트리는 <서비스-이름><네임스페이스=이름>.svc.cluster.local
형식을 갖는데,
컨테이너가 <서비스-이름>
만 갖는 경우에는 네임스페이스에 국한된 서비스로 연결된다.
이 기능은 개발, 스테이징 및 프로덕션과 같이
여러 네임스페이스 내에서 동일한 설정을 사용할 때 유용하다.
네임스페이스를 넘어서 접근하려면 전체 주소 도메인 이름(FQDN)을 사용해야 한다.
이 문서는 사용자가 쿠버네티스 네트워크폴리시 API를 사용하여 파드(Pod)가 서로 통신하는 방법을 제어하는 네트워크 폴리시를 선언하는데 도움을 준다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.8. 버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
네트워크 폴리시를 지원하는 네트워크 제공자를 구성하였는지 확인해야 한다. 다음과 같이 네트워크폴리시를 지원하는 많은 네트워크 제공자들이 있다.
nginx
디플로이먼트(Deployment)를 생성하고 서비스(Service)를 통해 노출하기쿠버네티스 네트워크 폴리시가 어떻게 동작하는지 확인하기 위해서, nginx
디플로이먼트를 생성한다.
kubectl create deployment nginx --image=nginx
deployment.apps/nginx created
nginx
라는 이름의 서비스를 통해 디플로이먼트를 노출한다.
kubectl expose deployment nginx --port=80
service/nginx exposed
위 명령어들은 nginx 파드에 대한 디플로이먼트를 생성하고, nginx
라는 이름의 서비스를 통해 디플로이먼트를 노출한다. nginx
파드와 디플로이먼트는 default
네임스페이스(namespace)에 존재한다.
kubectl get svc,pod
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes 10.100.0.1 <none> 443/TCP 46m
service/nginx 10.100.0.16 <none> 80/TCP 33s
NAME READY STATUS RESTARTS AGE
pod/nginx-701339712-e0qfq 1/1 Running 0 35s
사용자는 다른 파드에서 새 nginx
서비스에 접근할 수 있어야 한다. default
네임스페이스에 있는 다른 파드에서 nginx
서비스에 접근하기 위하여, busybox 컨테이너를 생성한다.
kubectl run busybox --rm -ti --image=busybox:1.28 -- /bin/sh
사용자 쉘에서, 다음의 명령을 실행한다.
wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
remote file exists
nginx
서비스에 대해 접근 제한하기access: true
레이블을 가지고 있는 파드만 nginx
서비스에 접근할 수 있도록 하기 위하여, 다음과 같은 네트워크폴리시 오브젝트를 생성한다.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: access-nginx
spec:
podSelector:
matchLabels:
app: nginx
ingress:
- from:
- podSelector:
matchLabels:
access: "true"
네트워크폴리시 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.
podSelector
를 포함한다. 사용자는 이 정책이 app=nginx
레이블을 갖는 파드를 선택하는 것을 볼 수 있다. 레이블은 nginx
디플로이먼트에 있는 파드에 자동으로 추가된다. 빈 podSelector
는 네임스페이스의 모든 파드를 선택한다.kubectl을 사용하여 위 nginx-policy.yaml
파일로부터 네트워크폴리시를 생성한다.
kubectl apply -f https://k8s.io/examples/service/networking/nginx-policy.yaml
networkpolicy.networking.k8s.io/access-nginx created
올바른 레이블이 없는 파드에서 nginx
서비스에 접근하려 할 경우, 요청 타임 아웃이 발생한다.
kubectl run busybox --rm -ti --image=busybox:1.28 -- /bin/sh
사용자 쉘에서, 다음의 명령을 실행한다.
wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
wget: download timed out
사용자는 요청이 허용되도록 하기 위하여 올바른 레이블을 갖는 파드를 생성한다.
kubectl run busybox --rm -ti --labels="access=true" --image=busybox:1.28 -- /bin/sh
사용자 쉘에서, 다음의 명령을 실행한다.
wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
remote file exists
이 페이지는 노드의 확장 리소스를 지정하는 방법을 보여준다. 확장 리소스를 통해 클러스터 관리자는 쿠버네티스에게 알려지지 않은 노드-레벨 리소스를 알릴 수 있다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
kubectl get nodes
이 연습에 사용할 노드 중 하나를 선택한다.
노드에서 새로운 확장 리소스를 알리려면, 쿠버네티스 API 서버에 HTTP PATCH 요청을 보낸다. 예를 들어, 노드 중 하나에 4개의 동글(dongle)이 있다고 가정한다. 다음은 노드에 4개의 동글 리소스를 알리는 PATCH 요청의 예이다.
PATCH /api/v1/nodes/<your-node-name>/status HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
Host: k8s-master:8080
[
{
"op": "add",
"path": "/status/capacity/example.com~1dongle",
"value": "4"
}
]
참고로 쿠버네티스는 동글이 무엇인지 또는 동글이 무엇을 위한 것인지 알 필요가 없다. 위의 PATCH 요청은 노드에 동글이라고 하는 네 가지 항목이 있음을 쿠버네티스에 알려준다.
쿠버네티스 API 서버에 요청을 쉽게 보낼 수 있도록 프록시를 시작한다.
kubectl proxy
다른 명령 창에서 HTTP PATCH 요청을 보낸다.
<your-node-name>
을 노드의 이름으로 바꾼다.
curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/example.com~1dongle", "value": "4"}]' \
http://localhost:8001/api/v1/nodes/<your-node-name>/status
~1
은 패치 경로의 / 문자에 대한
인코딩이다. JSON-Patch의 작업 경로값은 JSON-Pointer로
해석된다. 자세한 내용은 IETF RFC 6901의
섹션 3을 참고한다.출력은 노드가 4개의 동글 용량을 가졌음을 나타낸다.
"capacity": {
"cpu": "2",
"memory": "2049008Ki",
"example.com/dongle": "4",
노드의 정보를 확인한다.
kubectl describe node <your-node-name>
다시 한 번, 출력에 동글 리소스가 표시된다.
Capacity:
cpu: 2
memory: 2049008Ki
example.com/dongle: 4
이제, 애플리케이션 개발자는 특정 개수의 동글을 요청하는 파드를 만들 수 있다. 컨테이너에 확장 리소스 할당하기를 참고한다.
확장 리소스는 메모리 및 CPU 리소스와 비슷하다. 예를 들어, 노드에서 실행 중인 모든 컴포넌트가 공유할 특정 양의 메모리와 CPU가 노드에 있는 것처럼, 노드에서 실행 중인 모든 컴포넌트가 특정 동글을 공유할 수 있다. 또한 애플리케이션 개발자가 특정 양의 메모리와 CPU를 요청하는 파드를 생성할 수 있는 것처럼, 특정 동글을 요청하는 파드를 생성할 수 있다.
확장 리소스는 쿠버네티스에게 불투명하다. 쿠버네티스는 그것들이 무엇인지 전혀 모른다. 쿠버네티스는 노드에 특정 개수의 노드만 있다는 것을 알고 있다. 확장 리소스는 정수로 알려야 한다. 예를 들어, 노드는 4.5개의 동글이 아닌, 4개의 동글을 알릴 수 있다.
노드에 800GiB의 특별한 종류의 디스크 스토리지가 있다고 가정한다. example.com/special-storage와 같은 특별한 스토리지의 이름을 생성할 수 있다. 그런 다음 특정 크기, 100GiB의 청크로 알릴 수 있다. 이 경우, 노드에는 example.com/special-storage 유형의 8가지 리소스가 있다고 알린다.
Capacity:
...
example.com/special-storage: 8
이 특별한 스토리지에 대한 임의 요청을 허용하려면, 1바이트 크기의 청크로 특별한 스토리지를 알릴 수 있다. 이 경우, example.com/special-storage 유형의 800Gi 리소스를 알린다.
Capacity:
...
example.com/special-storage: 800Gi
그런 다음 컨테이너는 최대 800Gi의 임의 바이트 수의 특별한 스토리지를 요청할 수 있다.
다음은 노드에서 동글 알림을 제거하는 PATCH 요청이다.
PATCH /api/v1/nodes/<your-node-name>/status HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
Host: k8s-master:8080
[
{
"op": "remove",
"path": "/status/capacity/example.com~1dongle",
}
]
쿠버네티스 API 서버에 요청을 쉽게 보낼 수 있도록 프록시를 시작한다.
kubectl proxy
다른 명령 창에서 HTTP PATCH 요청을 보낸다.
<your-node-name>
을 노드의 이름으로 바꾼다.
curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "remove", "path": "/status/capacity/example.com~1dongle"}]' \
http://localhost:8001/api/v1/nodes/<your-node-name>/status
동글 알림이 제거되었는지 확인한다.
kubectl describe node <your-node-name> | grep dongle
(출력이 보이지 않아야 함)
이 페이지는 CoreDNS 업그레이드 프로세스와 kube-dns 대신 CoreDNS를 설치하는 방법을 보여준다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.9. 버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
CoreDNS는 쿠버네티스 클러스터의 DNS 역할을 수행할 수 있는, 유연하고 확장 가능한 DNS 서버이다. 쿠버네티스와 동일하게, CoreDNS 프로젝트도 CNCF가 관리한다.
사용자는 기존 디플로이먼트인 kube-dns를 교체하거나, 클러스터를 배포하고 업그레이드하는 kubeadm과 같은 툴을 사용하여 클러스터 안의 kube-dns 대신 CoreDNS를 사용할 수 있다.
Kube-dns의 배포나 교체에 관한 매뉴얼은 CoreDNS GitHub 프로젝트에 있는 문서를 확인하자.
쿠버네티스 버전 1.21에서, kubeadm은 DNS 애플리케이션으로서의 kube-dns
지원을 제거했다.
kubeadm
v1.31 버전에서는,
DNS 애플리케이션으로 CoreDNS만이 지원된다.
kube-dns
를 사용 중인 클러스터를 업그레이드하기 위하여
kubeadm
을 사용할 때 CoreDNS로 전환할 수 있다.
이 경우, kubeadm
은 kube-dns
컨피그맵(ConfigMap)을 기반으로
스텁 도메인(stub domain), 업스트림 네임 서버의 설정을 유지하며 CoreDNS 설정("Corefile")을 생성한다.
쿠버네티스에서의 CoreDNS 버전 페이지에서, 쿠버네티스 각 버전에 대해 kubeadm이 설치하는 CoreDNS의 버전을 확인할 수 있다.
CoreDNS만 업그레이드하고 싶거나 커스텀 이미지를 사용하고 싶은 경우, CoreDNS를 수동으로 업그레이드할 수 있다. 가이드라인 및 따라해보기를 참고하여 부드러운 업그레이드를 수행할 수 있다. 클러스터를 업그레이드할 때 기존 CoreDNS 환경 설정("Corefile")을 보존했는지 확인한다.
kubeadm
도구를 사용하여 클러스터를 업그레이드하는 경우,
kubeadm
이 자동으로 기존 CoreDNS 환경 설정을 보존한다.
리소스 활용이 중요한 경우, CoreDNS 구성을 조정하는 것이 유용할 수 있다. 더 자세한 내용은 CoreDNS 스케일링에 대한 설명서를 확인한다.
CoreDNS 환경 설정("Corefile")을 수정하여
kube-dns보다 더 많은 유스케이스를 지원하도록 CoreDNS를 구성할 수 있다.
더 많은 정보는 CoreDNS의 kubernetes
플러그인
문서를 참고하거나,
CoreDNS 블로그의
쿠버네티스를 위한 커스텀 DNS 엔트리를 확인한다.
이 예제는 네임스페이스(namespace)에서 사용되는 스토리지의 용량을 제한하는 방법을 보여준다.
예제에서는 다음과 같은 리소스가 사용된다. 리소스쿼터(ResourceQuota), 리밋레인지(LimitRange), 그리고 퍼시스턴트볼륨클레임(PersistentVolumeClaim).
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
클러스터 관리자는 사용자를 대표하여 클러스터를 운영하고 , 비용을 제어하기 위해 단일 네임스페이스에서 사용할 수 있는 스토리지의 크기를 제어하려고 한다.
관리자는 다음을 제한하려고 한다.
네임스페이스에 리밋레인지(LimitRange)
을 추가하면 스토리지 요청 크기가 최소 및 최대값으로 설정된다.
스토리지는 퍼시스턴트 볼륨 클레임(Persistent Volume Claim)을 통해 요청하게 된다.
제한 범위를 적용하는 어드미션 컨트롤러(Admission Controller)는 관리자가 설정한 값보다 높거나 낮은 PVC를 거부한다.
이 예제에서, 10Gi의 스토리지를 요청하는 PVC는 2Gi인 최대값을 초과하기 때문에 거부된다.
apiVersion: v1
kind: LimitRange
metadata:
name: storagelimits
spec:
limits:
- type: PersistentVolumeClaim
max:
storage: 2Gi
min:
storage: 1Gi
스토리지 요청에 대한 최솟값은 해당 스토리지의 제공자가 최소값을 특정하여 요구하는 경우 사용한다. 예를 들어, AWS EBS 볼륨을 사용할 때는 최소 1Gi를 요청해야 한다.
관리자는 네임스페이스의 PVC 수와 해당 PVC의 누적 용량을 제한할 수 있다. 최대값을 초과하는 새 PVC는 거부된다.
이 예제에서 네임스페이스의 6번째 PVC는 최대 카운트 5를 초과하기 때문에 거부된다. 또한, 위의 2Gi 최대 한계(max limit)와 결합된 5Gi 최대 할당량(maximum quota)은 각각 2Gi를 갖는 3개의 PVC를 가질 수 없다. 그것은 5Gi로 한도가 정해진 네임스페이스에 대해 6Gi의 요청이 될 것이다.
apiVersion: v1
kind: ResourceQuota
metadata:
name: storagequota
spec:
hard:
persistentvolumeclaims: "5"
requests.storage: "5Gi"
리밋레인지(LimitRange)을 지정하면 요청된 스토리지 양을 제한할 수 있으며 리소스쿼터(ResourceQuota)는 네임스페이스에 클레임(claim)수와 누적 스토리지 용량을 효과적으로 제한할 수 있다. 클러스터 관리자는 어느 프로젝트도 할당량을 초과하는 위험이 없도록 클러스터의 스토리지 예산을 계획할 수 있다.
API 서버, 스케줄러 및 컨트롤러 매니저와 같은 쿠버네티스 주요 컴포넌트들은 컨트롤 플레인 노드에서 동작한다. 반면, 애드온들은 일반 클러스터 노드에서 동작한다. 이러한 애드온들 중 일부(예: 메트릭 서버, DNS, UI)는 클러스터 전부가 정상적으로 동작하는 데 필수적일 수 있다. 만약, 필수 애드온이 축출되고(수동 축출, 혹은 업그레이드와 같은 동작으로 인한 의도하지 않은 축출) pending 상태가 된다면, 클러스터가 더 이상 제대로 동작하지 않을 수 있다. (사용률이 매우 높은 클러스터에서 해당 애드온이 축출되자마자 다른 대기중인 파드가 스케줄링되거나 다른 이유로 노드에서 사용할 수 있는 자원량이 줄어들어 pending 상태가 발생할 수 있다)
유의할 점은, 파드를 중요(critical)로 표시하는 것은 축출을 완전히 방지하기 위함이 아니다. 이것은 단지 파드가 영구적으로 사용할 수 없게 되는 것만을 방지하기 위함이다. 중요로 표시한 스태틱(static) 파드는 축출될 수 없다. 반면, 중요로 표시한 일반적인(non-static) 파드의 경우 항상 다시 스케줄링된다.
파드를 중요로 표시하기 위해서는, 해당 파드에 대해 priorityClassName을 system-cluster-critical
이나 system-node-critical
로 설정한다. system-node-critical
은 가장 높은 우선 순위를 가지며, 심지어 system-cluster-critical
보다도 우선 순위가 높다.
이 페이지는 클러스터 컨트롤 플레인의 특정한 API 버전을 활성화하거나 비활성화하는 방법에 대해 설명한다.
API 서버에 --runtime-config=api/<version>
커맨드 라인 인자를 사용함으로서 특정한 API 버전을
활성화하거나 비활성화할 수 있다. 이 인자에 대한 값으로는 콤마로 구분된 API 버전의 목록을 사용한다.
뒤쪽에 위치한 값은 앞쪽의 값보다 우선적으로 사용된다.
이 runtime-config
커맨드 라인 인자에는 다음의 두 개의 특수 키를 사용할 수도 있다.
api/all
: 사용할 수 있는 모든 API를 선택한다.api/legacy
: 레거시 API만을 선택한다. 여기서 레거시 API란 명시적으로
사용이 중단된 모든 API를 가리킨다.예를 들어서, v1을 제외한 모든 API 버전을 비활성화하기 위해서는 kube-apiserver
에
--runtime-config=api/all=false,api/v1=true
인자를 사용한다.
kube-apiserver
컴포넌트에 대한 더 자세한 내용은 다음의 문서
를 참고한다.
Kubernetes v1.21 [stable]
이 문서는 쿠버네티스 클러스터에서 sysctl 인터페이스를 사용하여 커널 파라미터를 어떻게 구성하고, 사용하는지를 설명한다.
/
또는 .
를
sysctl 이름의 구분자로 사용하는 것을 지원한다.
쿠버네티스 1.25 버전부터, 파드에 대해서도 sysctl을 설정할 때 슬래시 구분자를 지원하기 시작하였다.
예를 들어, 동일한 sysctl 이름을 kernel.shm_rmid_forced
와 같이 마침표를 구분자로 사용하여 나타내거나
kernel/shm_rmid_forced
와 같이 슬래시를 구분자로 사용하여 나타낼 수 있다.
sysctl 파라미터 변환에 대한 세부 사항은
리눅스 맨페이지 프로젝트의
sysctl.d(5) 페이지를 참고한다.쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
일부 단계에서는 실행 중인 클러스터의 kubelet에서 커맨드 라인 옵션을 재구성할 필요가 있다.
리눅스에서 sysctl 인터페이스는 관리자들이 런타임에 커널 파라미터를 수정할 수 있도록
허용한다. 파라미터는 /proc/sys
가상 파일 시스템을 통해 이용할 수 있다. 파라미터는
다음과 같은 다양한 서브 시스템을 포함한다.
kernel.
)net.
)vm.
)dev.
)모든 파라미터 리스트를 가져오려면 다음 명령을 실행한다.
sudo sysctl -a
sysctl은 safe sysctl과 unsafe sysctl로 구성되어 있다. safe sysctl은 적절한 네임스페이스 뿐만 아니라 동일한 노드의 파드 사이에 고립 되어야 한다. 즉, 하나의 파드에 safe sysctl을 설정한다는 것은 다음을 의미한다.
아직까지 대부분 네임스페이스된 sysctl은 safe sysctl로 고려되지 않았다. 다음 sysctl은 safe 명령을 지원한다.
kernel.shm_rmid_forced
,net.ipv4.ip_local_port_range
,net.ipv4.tcp_syncookies
,net.ipv4.ping_group_range
(쿠버네티스 1.18 이후),net.ipv4.ip_unprivileged_port_start
(쿠버네티스 1.22 이후).net.ipv4.tcp_syncookies
예시는 리눅스 커널 버전 4.4 또는 이하에서 네임스페이스되지 않는다.kubelet이 더 고립된 방법을 지원하면 추후 쿠버네티스 버전에서 확장될 것이다.
모든 safe sysctl은 기본적으로 활성화된다.
모든 unsafe sysctl은 기본적으로 비활성화되고, 노드별 기본 클러스터 관리자에 의해 수동으로 메뉴얼로 허용되어야 한다. unsafe sysctl이 비활성화된 파드는 스케줄링되지만, 시작에 실패한다.
위의 경고를 염두에 두고 클러스터 관리자는 고성능 또는 실시간 애플리케이션 조정과 같은 매우 특수한 상황에 대해 특정 unsafe sysctl을 허용할 수 있다. unsafe sysctl은 kubelet 플래그를 사용하여 노드별로 활성화된다. 예를 들면, 다음과 같다.
kubelet --allowed-unsafe-sysctls \
'kernel.msg*,net.core.somaxconn' ...
Minikube의 경우, extra-config
플래그를 통해 이 작업을 수행할 수 있다.
minikube start --extra-config="kubelet.allowed-unsafe-sysctls=kernel.msg*,net.core.somaxconn"...
네임스페이스 sysctl만 이 방법을 사용할 수 있다.
수많은 sysctl은 최근 리눅스 커널에서 네임스페이스 되어 있다. 이는 노드의 각 파드에 대해 개별적으로 설정할 수 있다는 것이다. 쿠버네티스의 파드 securityContext를 통해 네임스페이스 sysctl만 구성할 수 있다.
다음 sysctls는 네임스페이스로 알려져 있다. 이 목록은 이후 버전의 Linux 커널에서 변경될 수 있다.
kernel.shm*
,kernel.msg*
,kernel.sem
,fs.mqueue.*
,net.*
아래의 파라미터는 컨테이너 네트워킹 네임스페이스에서 설정할 수 있다.
그러나 예외가 존재한다. (예, net.netfilter.nf_conntrack_max
와 net.netfilter.nf_conntrack_expect_max
는
컨테이너 네트워킹 네임스페이스에서 설정되지만,
네임스페이스가 없다.)네임스페이스가 없는 sysctl은 node-level sysctl이라고 부른다. 이를 설정해야 한다면, 각 노드의 OS에서 수동으로 구성하거나 특권있는 컨테이너의 데몬셋을 사용하여야 한다.
네임스페이스 sysctl을 구성하기 위해서 파드 securityContext를 사용한다. securityContext는 동일한 파드의 모든 컨테이너에 적용된다.
이 예시는 safe sysctl kernel.shm_rmid_forced
와 두 개의 unsafe sysctl인
net.core.somaxconn
과 kernel.msgmax
를 설정하기 위해 파드 securityContext를 사용한다.
스펙에 따르면 safe sysctl과 unsafe sysctl 간
차이는 없다.
apiVersion: v1
kind: Pod
metadata:
name: sysctl-example
spec:
securityContext:
sysctls:
- name: kernel.shm_rmid_forced
value: "0"
- name: net.core.somaxconn
value: "1024"
- name: kernel.msgmax
value: "65536"
...
특별한 sysctl 설정이 있는 노드를 클러스터 내에서 _tainted_로 간주하고 sysctl 설정이 필요한 노드에만 파드를 예약하는 것이 좋다. 이를 구현하려면 쿠버네티스 테인트(taint)와 톨러레이션(toleration) 기능 을 사용하는 것이 좋다.
두 unsafe sysctl을 명시적으로 활성화하지 않은 노드에서 unsafe sysctl을 사용하는 파드가 시작되지 않는다. node-level sysctl과 마찬가지로 테인트와 톨러레이션 특징 또는 노드 테인트를 사용하여 해당 파드를 오른쪽 노드에 스케줄하는 것을 추천한다.
이 페이지는 쿠버네티스 클러스터를 업그레이드하기 위해 따라야 할 단계에 대한 개요를 제공한다.
클러스터를 업그레이드하는 방법은 초기 배포 방법에 의존적이며, 배포 이후 관련된 변경 사항에도 의존적일 수 있다.
고수준으로 살펴 본, 수행 단계
기존 클러스터가 존재해야 한다. 이 페이지는 쿠버네티스 1.30에서 쿠버네티스 1.31로 업그레이드하는 것에 관해 다룬다. 클러스터에서 쿠버네티스 1.30을 실행하지 않는 경우 업그레이드하려는 쿠버네티스 버전에 대한 설명서를 참고한다.
클러스터가 kubeadm
도구를 사용하여 배포된 경우
클러스터 업그레이드 방법에 대한 자세한 내용은
kubeadm 클러스터 업그레이드를 참조한다.
클러스터를 업그레이드한 후에는
kubectl
최신 버전을 설치해야 한다.
다음 순서에 따라 컨트롤 플레인을 수동으로 업데이트해야 한다.
이 때 kubectl
최신 버전을 설치
해야 한다.
클러스터의 각 노드에 대해 해당 노드를 드레인(drain) 한 다음 1.31 kubelet을 사용하는 새 노드로 바꾸거나 해당 노드의 kubelet을 업그레이드하고 노드를 다시 가동한다.
사용한 클러스터 배포 도구에 따라 해당 배포 도구가 제공하는 문서를 통하여, 유지 관리를 위해 권장되는 설정 단계를 확인한다.
클러스터에서 활성화된 쿠버네티스 리소스를 클러스터 내부적으로 표현(representation)하기 위해서 etcd로 직렬화된 객체는 특정 버전의 API를 사용하여 작성된다.
지원되는 API가 변경되면 이러한 개체를 새 API에서 다시 작성해야 할 수 있다. 이렇게 하지 않으면 결국 더 이상 디코딩할 수 없거나 쿠버네티스 API 서버에서 사용할 수 없는 리소스가 된다.
영향을 받는 각 객체를 지원되는 최신 API를 사용하여 가져온(fetch) 다음, 해당 API를 사용하여 다시 쓴다.
새로운 쿠버네티스 버전으로 업그레이드하면 새로운 API를 제공할 수 있다.
kubectl convert
명령을 사용하여 서로 다른 API 버전 간에 매니페스트를 변환할 수 있다.
예시:
kubectl convert -f pod.yaml --output-version v1
kubectl
도구는 pod.yaml
의 내용을 kind
를 파드(변경되지 않음, unchanged)로 설정하는 매니페스트로 대체하고,
수정된 apiVersion
으로 대체한다.
클러스터가 장치 플러그인을 실행 중이고 노드를 최신 장치 플러그인 API 버전이 있는 쿠버네티스 릴리스로 업그레이드해야 하는 경우, 업그레이드 중에 장치 할당이 계속 성공적으로 완료되도록 하려면 장치 플러그인을 업그레이드해야 한다.
API 호환성 및 Kubelet 장치 매니저 API 버전을 참조한다.
이 페이지는 쿠버네티스 클러스터에서 DNS 서비스의 오토스케일링을 구성하고 활성화하는 방법을 보여준다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
이 가이드는 노드가 AMD64 또는 인텔 64 CPU 아키텍처를 사용한다고 가정한다.
Kubernetes DNS가 활성화되어 있는지 확인한다.
kube-system 네임스페이스 에서 클러스터의 디플로이먼트를 나열한다.
kubectl get deployment --namespace=kube-system
출력은 다음과 유사하다.
NAME READY UP-TO-DATE AVAILABLE AGE
...
dns-autoscaler 1/1 1 1 ...
...
해당 출력에서 "dns-autoscaler"가 표시되면, DNS 수평 오토스케일링이 이미 활성화되어 있다는 의미이므로, 오토스케일링 파라미터 조정으로 건너뛰면 된다.
kube-system 네임스페이스에서 클러스터의 DNS 디플로이먼트를 나열한다.
kubectl get deployment -l k8s-app=kube-dns --namespace=kube-system
출력은 이와 유사하다.
NAME READY UP-TO-DATE AVAILABLE AGE
...
coredns 2/2 2 2 ...
...
DNS 서비스용 디플로이먼트가 표시되지 않으면, 이름으로 찾을 수 있다.
kubectl get deployment --namespace=kube-system
그리고 coredns
또는 kube-dns
라는 디플로이먼트를 찾는다.
스케일 대상은
Deployment/<your-deployment-name>
이며, 여기서 <your-deployment-name>
는 DNS 디플로이먼트의 이름이다. 예를 들어,
DNS용 디플로이먼트 이름이 coredns인 경우, 스케일 대상은 Deployment/coredns이다.
k8s-app=kube-dns
로 레이블을 설정한다.이 섹션에서는 새로운 디플로이먼트를 만든다. 디플로이먼트의 파드는
cluster-proportional-autoscaler-amd64
이미지 기반의 컨테이너를 실행한다.
다음의 내용으로 dns-horizontal-autoscaler.yaml
라는 파일을 만든다.
kind: ServiceAccount
apiVersion: v1
metadata:
name: kube-dns-autoscaler
namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: system:kube-dns-autoscaler
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list", "watch"]
- apiGroups: [""]
resources: ["replicationcontrollers/scale"]
verbs: ["get", "update"]
- apiGroups: ["apps"]
resources: ["deployments/scale", "replicasets/scale"]
verbs: ["get", "update"]
# 아래 문제가 해결되면 configmaps 규칙을 제거한다.
# kubernetes-incubator/cluster-proportional-autoscaler#16
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "create"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: system:kube-dns-autoscaler
subjects:
- kind: ServiceAccount
name: kube-dns-autoscaler
namespace: kube-system
roleRef:
kind: ClusterRole
name: system:kube-dns-autoscaler
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kube-dns-autoscaler
namespace: kube-system
labels:
k8s-app: kube-dns-autoscaler
kubernetes.io/cluster-service: "true"
spec:
selector:
matchLabels:
k8s-app: kube-dns-autoscaler
template:
metadata:
labels:
k8s-app: kube-dns-autoscaler
spec:
priorityClassName: system-cluster-critical
securityContext:
seccompProfile:
type: RuntimeDefault
supplementalGroups: [ 65534 ]
fsGroup: 65534
nodeSelector:
kubernetes.io/os: linux
containers:
- name: autoscaler
image: registry.k8s.io/cpa/cluster-proportional-autoscaler:1.8.4
resources:
requests:
cpu: "20m"
memory: "10Mi"
command:
- /cluster-proportional-autoscaler
- --namespace=kube-system
- --configmap=kube-dns-autoscaler
# 타겟은 cluster/addons/dns/kube-dns.yaml.base와 동기화된 상태로 유지해야 한다.
- --target=<SCALE_TARGET>
# 클러스터가 대규모 노드(많은 코어가 있는)를 사용하는 경우, "coresPerReplica"가 우선시해야 한다.
# 작은 노드를 사용하는 경우, "nodesPerReplica"가 우선시해야 한다.
- --default-params={"linear":{"coresPerReplica":256,"nodesPerReplica":16,"preventSinglePointFailure":true,"includeUnschedulableNodes":true}}
- --logtostderr=true
- --v=2
tolerations:
- key: "CriticalAddonsOnly"
operator: "Exists"
serviceAccountName: kube-dns-autoscaler
파일에서, <SCALE_TARGET>
을 사용자의 스케일 대상으로 변경한다.
구성 파일이 포함된 디렉토리로 이동하고, 디플로이먼트를 만들기 위해 다음의 커맨드를 입력한다.
kubectl apply -f dns-horizontal-autoscaler.yaml
성공적인 커맨드의 출력은 다음과 같다.
deployment.apps/dns-autoscaler created
DNS 수평 오토스케일링이 활성화되었다.
dns-autoscaler 컨피그맵이 있는지 확인한다.
kubectl get configmap --namespace=kube-system
출력은 다음과 유사하다.
NAME DATA AGE
...
dns-autoscaler 1 ...
...
컨피그맵에서 데이터를 수정한다.
kubectl edit configmap dns-autoscaler --namespace=kube-system
다음에 해당하는 줄을 찾는다.
linear: '{"coresPerReplica":256,"min":1,"nodesPerReplica":16}'
필요에 따라서 해당 필드를 수정한다. "min" 필드는 최소 DNS 백엔드 수를 나타낸다. 실제 백엔드의 수는 이 방정식을 사용하여 계산된다.
replicas = max( ceil( cores × 1/coresPerReplica ) , ceil( nodes × 1/nodesPerReplica ) )
coresPerReplica
및 nodesPerReplica
값은 모두
부동 소수점이니 주의한다.
해당 아이디어는 클러스터가 코어가 많은 노드를 사용하는 경우,
coresPerReplica
의 영향을 더 크게 만들고, 코어 수가 적은 노드를 사용하는 경우
nodesPerReplica
의 영향을 더 크게 만드는 것이다.
그밖에 다른 스케일링 패턴도 지원한다. cluster-proportional-autoscaler를 참고한다.
DNS 수평 오토스케일링을 조정하기 위해 몇 가지 옵션이 있다. 사용할 옵션은 조건에 따라 다르다.
이 옵션은 모든 상황에서 작동한다. 다음 커맨드를 입력한다.
kubectl scale deployment --replicas=0 dns-autoscaler --namespace=kube-system
출력은 다음과 같다.
deployment.apps/dns-autoscaler scaled
레플리카 수가 0인지 확인한다.
kubectl get rs --namespace=kube-system
출력은 DESIRED 및 CURRENT 열에 0으로 보여준다.
NAME DESIRED CURRENT READY AGE
...
dns-autoscaler-6b59789fc8 0 0 0 ...
...
이 옵션은 dns-autoscaler가 자체적으로 제어되는 경우 작동하며, 아무도 이것을 재-생성하지 않음을 의미한다.
kubectl delete deployment dns-autoscaler --namespace=kube-system
출력은 다음과 같다.
deployment.apps "dns-autoscaler" deleted
이 옵션은 dns-autoscaler가 (사용 중단되(deprecated)) 애드온 매니저 의 제어를 받고 마스터 노드에 쓰기 권한이 있는 경우 작동한다.
마스터 노드에 로그인하고 해당 매니페스트 파일을 삭제한다. 이 dns-autoscaler의 일반 경로는 다음과 같다.
/etc/kubernetes/addons/dns-horizontal-autoscaler/dns-horizontal-autoscaler.yaml
매니페스트 파일이 삭제된 후, 애드온 매니저는 dns-autoscaler 디플로이먼트를 삭제한다.
cluster-proportional-autoscaler 애플리케이션은 DNS 서비스와 별도로 배포된다.
오토스케일러 파드는 클러스터의 노드 및 코어 수에 대해 쿠버네티스 API 서버를 폴링(poll)하는 클라이언트를 실행한다.
의도한 레플리카 수는 주어진 스케일링 파라미터로 계산하고 예약 가능한 노드 및 코어를 기반으로 DNS 백엔드에 적용한다.
스케일링 파마리터와 데이터 포인트는 컨피그맵을 통해 자동 오토스케일러에게 제공된다.그리고, 의도한 최근 스케일링 파라미터로 최신 상태가 되도록 폴링 간격에 따라 파라미터 표를 갱신한다.
오토스케일러 파드를 다시 빌드 또는 재 시작하지 않고도 스케일링 파라미터를 변경할 수 있다.
오토스케일러는 linear와 ladder 두 가지 제어 패턴을 지원하는 컨트롤러 인터페이스를 제공한다.
이 페이지에서는 가비지 수집 중 클러스터에서 사용할 캐스케이딩 삭제 타입을 지정하는 방법을 보여준다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
또한 다양한 타입들의 캐스케이딩 삭제를 실험하려면 샘플 디플로이먼트를 생성할 필요가 있다. 각 타입에 대해 디플로이먼트를 다시 생성해야 할 수도 있다.
파드에서 ownerReferences
필드가 존재하는지 확인한다.
kubectl get pods -l app=nginx --output=yaml
출력은 다음과 같이 ownerReferences
필드를 가진다.
apiVersion: v1
...
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: nginx-deployment-6b474476c4
uid: 4fdcd81c-bd5d-41f7-97af-3a3b759af9a7
...
기본적으로 쿠버네티스는 종속 오브젝트를 삭제하기 위해서
백그라운드 캐스케이딩 삭제를 사용한다. 클러스터를 실행하는 쿠버네티스 버전에 따라
kubectl
또는 쿠버네티스 API를 사용해
포그라운드 캐스케이딩 삭제로 전환할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행 kubectl version
.
kubectl
또는 쿠버네티스 API를 사용해
포그라운드 캐스케이딩 삭제로 오브젝트들을 삭제할 수 있다.
kubectl 사용
다음 명령어를 실행한다.
kubectl delete deployment nginx-deployment --cascade=foreground
쿠버네티스 API 사용
로컬 프록시 세션을 시작한다.
kubectl proxy --port=8080
삭제를 작동시키기 위해 curl
을 사용한다.
curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/deployments/nginx-deployment \
-d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
-H "Content-Type: application/json"
출력에는 다음과 같이
foregroundDeletion
파이널라이저(finalizer)가 포함되어 있다.
"kind": "Deployment",
"apiVersion": "apps/v1",
"metadata": {
"name": "nginx-deployment",
"namespace": "default",
"uid": "d1ce1b02-cae8-4288-8a53-30e84d8fa505",
"resourceVersion": "1363097",
"creationTimestamp": "2021-07-08T20:24:37Z",
"deletionTimestamp": "2021-07-08T20:27:39Z",
"finalizers": [
"foregroundDeletion"
]
...
kubectl
또는 쿠버네티스 API를 사용한다.
버전 확인을 위해서, 다음 커맨드를 실행 kubectl version
.
kubectl
또는 쿠버네티스 API를 사용해
백그라운드 캐스케이딩 삭제로 오브젝트들을 삭제할 수 있다.
쿠버네티스는 기본적으로 백그라운드 캐스케이딩 삭제를 사용하므로, --cascade
플래그
또는 propagationPolicy
인수 없이
다음 명령을 실행해도 같은 작업을 수행한다.
kubectl 사용
다음 명령어를 실행한다.
kubectl delete deployment nginx-deployment --cascade=background
쿠버네티스 API 사용
로컬 프록시 세션을 시작한다.
kubectl proxy --port=8080
삭제를 작동시키기 위해 curl
을 사용한다.
curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/deployments/nginx-deployment \
-d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Background"}' \
-H "Content-Type: application/json"
출력은 다음과 유사하다.
"kind": "Status",
"apiVersion": "v1",
...
"status": "Success",
"details": {
"name": "nginx-deployment",
"group": "apps",
"kind": "deployments",
"uid": "cc9eefb9-2d49-4445-b1c1-d261c9396456"
}
기본적으로, 쿠버네티스에 오브젝트를 삭제하도록 지시하면
컨트롤러는
종속 오브젝트들도 제거한다. 클러스터를 실행하는
쿠버네티스 버전에 따라 kubectl
또는 쿠버네티스 API를 사용해
종속 오브젝트를 쿠버네티스 고아로 만들 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행 kubectl version
.
kubectl 사용
다음 명령어를 실행한다.
kubectl delete deployment nginx-deployment --cascade=orphan
쿠버네티스 API 사용
로컬 프록시 세션을 시작한다.
kubectl proxy --port=8080
삭제를 작동시키기 위해 curl
을 사용한다.
curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/deployments/nginx-deployment \
-d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
-H "Content-Type: application/json"
출력에는 다음과 같이 finalizers
필드에 orphan
이 포함되어 있다.
"kind": "Deployment",
"apiVersion": "apps/v1",
"namespace": "default",
"uid": "6f577034-42a0-479d-be21-78018c466f1f",
"creationTimestamp": "2021-07-09T16:46:37Z",
"deletionTimestamp": "2021-07-09T16:47:08Z",
"deletionGracePeriodSeconds": 0,
"finalizers": [
"orphan"
],
...
디플로이먼트가 관리하는 파드들이 계속 실행 중인지 확인할 수 있다.
kubectl get pods -l app=nginx
이 페이지는 쿠버네티스 퍼시트턴트볼륨(PersistentVolume)의 반환 정책을 변경하는 방법을 보여준다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
퍼시스턴트볼륨은 "Retain(보존)", "Recycle(재활용)", "Delete(삭제)" 를 포함한
다양한 반환 정책을 갖는다. 동적으로 프로비저닝 된 퍼시스턴트볼륨의 경우
기본 반환 정책은 "Delete" 이다. 이는 사용자가 해당 PersistentVolumeClaim
을 삭제하면,
동적으로 프로비저닝 된 볼륨이 자동적으로 삭제됨을 의미한다.
볼륨에 중요한 데이터가 포함된 경우, 이러한 자동 삭제는 부적절 할 수 있다.
이 경우에는, "Retain" 정책을 사용하는 것이 더 적합하다.
"Retain" 정책에서, 사용자가 퍼시스턴트볼륨클레임을 삭제할 경우 해당하는
퍼시스턴트볼륨은 삭제되지 않는다.
대신, Released
단계로 이동되어, 모든 데이터를 수동으로 복구할 수 있다.
사용자의 클러스터에서 퍼시스턴트볼륨을 조회한다.
kubectl get pv
결과는 아래와 같다.
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-b6efd8da-b7b5-11e6-9d58-0ed433a7dd94 4Gi RWO Delete Bound default/claim1 manual 10s
pvc-b95650f8-b7b5-11e6-9d58-0ed433a7dd94 4Gi RWO Delete Bound default/claim2 manual 6s
pvc-bb3ca71d-b7b5-11e6-9d58-0ed433a7dd94 4Gi RWO Delete Bound default/claim3 manual 3s
이 목록은 동적으로 프로비저닝 된 볼륨을 쉽게 식별할 수 있도록 각 볼륨에 바인딩 되어 있는 퍼시스턴트볼륨클레임(PersistentVolumeClaim)의 이름도 포함한다.
사용자의 퍼시스턴트볼륨 중 하나를 선택한 후에 반환 정책을 변경한다.
kubectl patch pv <your-pv-name> -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
여기서 <your-pv-name>
는 사용자가 선택한 퍼시스턴트볼륨의 이름이다.
윈도우에서는, 공백이 포함된 모든 JSONPath 템플릿에 겹 따옴표를 사용해야 한다. (bash에 대해 위에서 표시된 홑 따옴표가 아니다.) 따라서 템플릿의 모든 표현식에서 홑 따옴표를 쓰거나, 이스케이프 처리된 겹 따옴표를 써야 한다. 예를 들면 다음과 같다.
kubectl patch pv <your-pv-name> -p "{\"spec\":{\"persistentVolumeReclaimPolicy\":\"Retain\"}}"
선택한 PersistentVolume이 올바른 정책을 갖는지 확인한다.
kubectl get pv
결과는 아래와 같다.
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-b6efd8da-b7b5-11e6-9d58-0ed433a7dd94 4Gi RWO Delete Bound default/claim1 manual 40s
pvc-b95650f8-b7b5-11e6-9d58-0ed433a7dd94 4Gi RWO Delete Bound default/claim2 manual 36s
pvc-bb3ca71d-b7b5-11e6-9d58-0ed433a7dd94 4Gi RWO Retain Bound default/claim3 manual 33s
위 결과에서, default/claim3
클레임과 바인딩 되어 있는 볼륨이 Retain
반환 정책을
갖는 것을 볼 수 있다. 사용자가 default/claim3
클레임을 삭제할 경우,
볼륨은 자동으로 삭제 되지 않는다.
.spec.persistentVolumeReclaimPolicy
필드에
주의한다.이 페이지는 메모리 요청량 과 메모리 상한 을 컨테이너에 어떻게 지정하는지 보여준다. 컨테이너는 요청량 만큼의 메모리 확보가 보장되나 상한보다 더 많은 메모리는 사용할 수 없다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
클러스터의 각 노드에 최소 300 MiB 메모리가 있어야 한다.
이 페이지의 몇 가지 단계를 수행하기 위해서는 클러스터 내 metrics-server 서비스 실행이 필요하다. 이미 실행 중인 metrics-server가 있다면 다음 단계를 건너뛸 수 있다.
Minikube를 사용 중이라면, 다음 명령어를 실행해 metric-server를 활성화할 수 있다.
minikube addons enable metrics-server
metric-server가 실행 중인지 확인하거나 다른 제공자의 리소스 메트릭 API (metrics.k8s.io
)를 확인하기 위해
다음의 명령어를 실행한다.
kubectl get apiservices
리소스 메트릭 API를 사용할 수 있다면 출력에
metrics.k8s.io
에 대한 참조가 포함되어 있다.
NAME
v1beta1.metrics.k8s.io
이 예제에서 생성할 자원과 클러스터 내 나머지를 분리하기 위해 네임스페이스를 생성한다.
kubectl create namespace mem-example
컨테이너에 메모리 요청량을 지정하기 위해서는 컨테이너의 리소스 매니페스트에
resources:requests
필드를 포함한다. 리소스 상한을 지정하기 위해서는
resources:limits
필드를 포함한다.
이 예제에서 하나의 컨테이너를 가진 파드를 생성한다. 생성된 컨테이너는 100 MiB 메모리 요청량과 200 MiB 메모리 상한을 갖는다. 이 것이 파드 구성 파일이다.
apiVersion: v1
kind: Pod
metadata:
name: memory-demo
namespace: mem-example
spec:
containers:
- name: memory-demo-ctr
image: polinux/stress
resources:
requests:
memory: "100Mi"
limits:
memory: "200Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
구성 파일 내 args
섹션은 컨테이너가 시작될 때 아규먼트를 제공한다.
"--vm-bytes", "150M"
아규먼트는 컨테이너가 150 MiB 할당을 시도 하도록 한다.
파드 생성:
kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit.yaml --namespace=mem-example
파드 컨테이너가 실행 중인지 확인:
kubectl get pod memory-demo --namespace=mem-example
파드에 대한 자세한 정보 보기:
kubectl get pod memory-demo --output=yaml --namespace=mem-example
출력은 파드 내 하나의 컨테이너에 100MiB 메모리 요청량과 200 MiB 메모리 상한이 있는 것을 보여준다.
...
resources:
requests:
memory: 100Mi
limits:
memory: 200Mi
...
kubectl top
을 실행하여 파드 메트릭 가져오기:
kubectl top pod memory-demo --namespace=mem-example
출력은 파드가 약 150 MiB 해당하는 약 162,900,000 바이트 메모리를 사용하는 것을 보여준다. 이는 파드의 100 MiB 요청 보다 많으나 파드의 200 MiB 상한보다는 적다.
NAME CPU(cores) MEMORY(bytes)
memory-demo <something> 162856960
파드 삭제:
kubectl delete pod memory-demo --namespace=mem-example
노드 내 메모리가 충분하다면 컨테이너는 지정한 요청량보다 많은 메모리를 사용 할 수 있다. 그러나 컨테이너는 지정한 메모리 상한보다 많은 메모리를 사용할 수 없다. 만약 컨테이너가 지정한 메모리 상한보다 많은 메모리를 할당하면 해당 컨테이너는 종료 대상 후보가 된다. 만약 컨테이너가 지속적으로 지정된 상한보다 많은 메모리를 사용한다면, 해당 컨테이너는 종료된다. 만약 종료된 컨테이너가 재실행 가능하다면 다른 런타임 실패와 마찬가지로 kubelet에 의해 재실행된다.
이 예제에서는 상한보다 많은 메모리를 할당하려는 파드를 생성한다. 이 것은 50 MiB 메모리 요청량과 100 MiB 메모리 상한을 갖는 하나의 컨테이너를 갖는 파드의 구성 파일이다.
apiVersion: v1
kind: Pod
metadata:
name: memory-demo-2
namespace: mem-example
spec:
containers:
- name: memory-demo-2-ctr
image: polinux/stress
resources:
requests:
memory: "50Mi"
limits:
memory: "100Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]
구성 파일의 args
섹션에서 컨테이너가
100 MiB 상한을 훨씬 초과하는 250 MiB의 메모리를 할당하려는 것을 볼 수 있다.
파드 생성:
kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit-2.yaml --namespace=mem-example
파드에 대한 자세한 정보 보기:
kubectl get pod memory-demo-2 --namespace=mem-example
이 시점에 컨테이너가 실행되거나 종료되었을 수 있다. 컨테이너가 종료될 때까지 이전의 명령을 반복한다.
NAME READY STATUS RESTARTS AGE
memory-demo-2 0/1 OOMKilled 1 24s
컨테이너 상태의 상세 상태 보기:
kubectl get pod memory-demo-2 --output=yaml --namespace=mem-example
컨테이너가 메모리 부족 (OOM) 으로 종료되었음이 출력된다.
lastState:
terminated:
containerID: 65183c1877aaec2e8427bc95609cc52677a454b56fcb24340dbd22917c23b10f
exitCode: 137
finishedAt: 2017-06-20T20:52:19Z
reason: OOMKilled
startedAt: null
이 예제에서 컨테이너는 재실행 가능하여 kubelet에 의해 재실행된다. 컨테이너가 종료되었다 재실행되는 것을 보기 위해 다음 명령을 몇 번 반복한다.
kubectl get pod memory-demo-2 --namespace=mem-example
출력은 컨테이너의 종료, 재실행, 재종료, 재실행 등을 보여준다.
kubectl get pod memory-demo-2 --namespace=mem-example
NAME READY STATUS RESTARTS AGE
memory-demo-2 0/1 OOMKilled 1 37s
kubectl get pod memory-demo-2 --namespace=mem-example
NAME READY STATUS RESTARTS AGE
memory-demo-2 1/1 Running 2 40s
파드 내역에 대한 상세 정보 보기:
kubectl describe pod memory-demo-2 --namespace=mem-example
컨테이너가 반복적으로 시작하고 실패 하는 출력을 보여준다.
... Normal Created Created container with id 66a3a20aa7980e61be4922780bf9d24d1a1d8b7395c09861225b0eba1b1f8511
... Warning BackOff Back-off restarting failed container
클러스터 노드에 대한 자세한 정보 보기:
kubectl describe nodes
출력에는 컨테이너가 메모리 부족으로 종료된 기록이 포함된다.
Warning OOMKilling Memory cgroup out of memory: Kill process 4481 (stress) score 1994 or sacrifice child
파드 삭제:
kubectl delete pod memory-demo-2 --namespace=mem-example
메모리 요청량과 상한은 컨테이너와 관련있지만, 파드가 가지는 메모리 요청량과 상한으로 이해하면 유용하다. 파드의 메모리 요청량은 파드 내 모든 컨테이너의 메모리 요청량의 합이다. 마찬가지로 파드의 메모리 상한은 파드 내 모든 컨테이너의 메모리 상한의 합이다.
파드는 요청량을 기반하여 스케줄링된다. 노드에 파드의 메모리 요청량을 충족하기에 충분한 메모리가 있는 경우에만 파드가 노드에서 스케줄링된다.
이 예제에서는 메모리 요청량이 너무 커 클러스터 내 모든 노드의 용량을 초과하는 파드를 생성한다. 다음은 클러스터 내 모든 노드의 용량을 초과할 수 있는 1000 GiB 메모리 요청을 포함하는 컨테이너를 갖는 파드의 구성 파일이다.
apiVersion: v1
kind: Pod
metadata:
name: memory-demo-3
namespace: mem-example
spec:
containers:
- name: memory-demo-3-ctr
image: polinux/stress
resources:
requests:
memory: "1000Gi"
limits:
memory: "1000Gi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
파드 생성:
kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit-3.yaml --namespace=mem-example
파드 상태 보기:
kubectl get pod memory-demo-3 --namespace=mem-example
파드 상태가 PENDING 상태임이 출력된다. 즉 파드는 어떤 노드에서도 실행되도록 스케줄 되지 않고 PENDING가 계속 지속된다.
kubectl get pod memory-demo-3 --namespace=mem-example
NAME READY STATUS RESTARTS AGE
memory-demo-3 0/1 Pending 0 25s
이벤트를 포함한 파드 상세 정보 보기:
kubectl describe pod memory-demo-3 --namespace=mem-example
출력은 노드 내 메모리가 부족하여 파드가 스케줄링될 수 없음을 보여준다.
Events:
... Reason Message
------ -------
... FailedScheduling No nodes are available that match all of the following predicates:: Insufficient memory (3).
메모리 리소스는 byte 단위로 측정된다. 다음 접미사 중 하나로 정수 또는 고정 소수점으로 메모리를 표시할 수 있다. E, P, T, G, M, K, Ei, Pi, Ti, Gi, Mi, Ki. 예를 들어 다음은 거의 유사한 값을 나타낸다.
128974848, 129e6, 129M, 123Mi
파드 삭제:
kubectl delete pod memory-demo-3 --namespace=mem-example
컨테이너에 메모리 상한을 지정하지 않으면 다음 중 하나가 적용된다.
컨테이너가 사용할 수 있는 메모리 상한은 없다. 컨테이너가 실행 중인 노드에서 사용 가능한 모든 메모리를 사용하여 OOM Killer가 실행될 수 있다. 또한 메모리 부족으로 인한 종료 시 메모리 상한이 없는 컨테이너가 종료될 가능성이 크다.
기본 메모리 상한을 갖는 네임스페이스 내에서 실행중인 컨테이너는 자동으로 기본 메모리 상한이 할당된다. 클러스터 관리자들은 LimitRange를 사용해 메모리 상한의 기본 값을 지정 가능하다.
클러스터에서 실행되는 컨테이너에 메모리 요청량과 상한을 구성하여 클러스터 내 노드들의 메모리 리소스를 효율적으로 사용할 수 있게 할 수 있다. 파드의 메모리 요청량을 적게 유지하여 파드가 높은 확률로 스케줄링 될 수 있도록 한다. 메모리 상한이 메모리 요청량보다 크면 다음 두 가지가 수행된다.
네임스페이스를 지운다. 이 작업을 통해 네임스페이스 내 생성했던 모든 파드들은 삭제된다.
kubectl delete namespace mem-example
Kubernetes v1.18 [stable]
이 페이지에서는 윈도우 노드에서 실행될 파드 및 컨테이너에 runAsUserName
설정을 사용하는 방법을 소개한다. 이는 리눅스 관련 runAsUser
설정과 거의 동일하여, 컨테이너의 기본값과 다른 username으로 애플리케이션을 실행할 수 있다.
쿠버네티스 클러스터가 있어야 하며 클러스터와 통신하도록 kubectl 명령줄 도구를 구성해야 한다. 클러스터에는 윈도우 워커 노드가 있어야 하고, 해당 노드에서 윈도우 워크로드를 실행하는 컨테이너의 파드가 스케쥴 된다.
파드의 컨테이너 프로세스를 실행할 username을 지정하려면 파드 명세에 securityContext
필드 (PodSecurityContext) 를 포함시키고, 그 안에 runAsUserName
필드를 포함하는 windowsOptions
(WindowsSecurityContextOptions) 필드를 추가한다.
파드에 지정하는 윈도우 보안 컨텍스트 옵션은 파드의 모든 컨테이너 및 초기화 컨테이너에 적용된다.
다음은 runAsUserName
필드가 설정된 윈도우 파드의 구성 파일이다.
apiVersion: v1
kind: Pod
metadata:
name: run-as-username-pod-demo
spec:
securityContext:
windowsOptions:
runAsUserName: "ContainerUser"
containers:
- name: run-as-username-demo
image: mcr.microsoft.com/windows/servercore:ltsc2019
command: ["ping", "-t", "localhost"]
nodeSelector:
kubernetes.io/os: windows
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/windows/run-as-username-pod.yaml
파드의 컨테이너가 실행 중인지 확인한다.
kubectl get pod run-as-username-pod-demo
실행 중인 컨테이너의 셸에 접근한다.
kubectl exec -it run-as-username-pod-demo -- powershell
셸이 올바른 username인 사용자로 실행 중인지 확인한다.
echo $env:USERNAME
결과는 다음과 같다.
ContainerUser
컨테이너의 프로세스를 실행할 username을 지정하려면, 컨테이너 매니페스트에 securityContext
필드 (SecurityContext) 를 포함시키고 그 안에 runAsUserName
필드를 포함하는 windowsOptions
(WindowsSecurityContextOptions) 필드를 추가한다.
컨테이너에 지정하는 윈도우 보안 컨텍스트 옵션은 해당 개별 컨테이너에만 적용되며 파드 수준에서 지정한 설정을 재정의한다.
다음은 한 개의 컨테이너에 runAsUserName
필드가 파드 수준 및 컨테이너 수준에서 설정되는 파드의 구성 파일이다.
apiVersion: v1
kind: Pod
metadata:
name: run-as-username-container-demo
spec:
securityContext:
windowsOptions:
runAsUserName: "ContainerUser"
containers:
- name: run-as-username-demo
image: mcr.microsoft.com/windows/servercore:ltsc2019
command: ["ping", "-t", "localhost"]
securityContext:
windowsOptions:
runAsUserName: "ContainerAdministrator"
nodeSelector:
kubernetes.io/os: windows
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/windows/run-as-username-container.yaml
파드의 컨테이너가 실행 중인지 확인한다.
kubectl get pod run-as-username-container-demo
실행 중인 컨테이너의 셸에 접근한다.
kubectl exec -it run-as-username-container-demo -- powershell
셸이 사용자에게 올바른 username(컨테이너 수준에서 설정된 사용자)을 실행 중인지 확인한다.
echo $env:USERNAME
결과는 다음과 같다.
ContainerAdministrator
이 기능을 사용하려면 runAsUserName
필드에 설정된 값이 유효한 username이어야 한다. 형식은 DOMAIN\USER
여야하고, 여기서 DOMAIN\
은 선택 사항이다. 윈도우 username은 대소문자를 구분하지 않는다. 또한 DOMAIN
및 USER
와 관련된 몇 가지 제약사항이 있다.
runAsUserName
필드는 비워 둘 수 없으며 제어 문자를 포함할 수 없다. (ASCII 값: 0x00-0x1F
, 0x7F
)DOMAIN
은 NetBios 이름 또는 DNS 이름이어야 하며 각각 고유한 제한이 있다.
.
(마침표)으로 시작할 수 없으며 다음 문자를 포함할 수 없다. \ / : * ? " < > |
.
), 대시(-
)로만 구성되며, 마침표 또는 대시로 시작하거나 끝날 수 없다.USER
는 최대 20자이며, 오직 마침표나 공백들로는 구성할 수 없고, 다음 문자는 포함할 수 없다. " / \ [ ] : ; | = , + * ? < > @
.runAsUserName
필드에 허용되는 값의 예 : ContainerAdministrator
,ContainerUser
, NT AUTHORITY\NETWORK SERVICE
, NT AUTHORITY\LOCAL SERVICE
.
이러한 제약사항에 대한 자세한 내용은 여기 와 여기를 확인한다.
Kubernetes v1.18 [stable]
이 페이지는 윈도우 노드에서 실행되는 파드와 컨테이너용으로 그룹 관리 서비스 어카운트(Group Managed Service Accounts, GMSA)를 구성하는 방법을 소개한다. 그룹 관리 서비스 어카운트는 자동 암호 관리, 단순화된 서비스 사용자 이름(service principal name, SPN) 관리, 여러 서버에 걸쳐 다른 관리자에게 관리를 위임하는 기능을 제공하는 특정한 유형의 액티브 디렉터리(Active Directory) 계정이다.
쿠버네티스에서 GMSA 자격 증명 사양은 쿠버네티스 클러스터 전체 범위에서 사용자 정의 리소스(Custom Resources)로 구성된다. 윈도우 파드 및 파드 내의 개별 컨테이너들은 다른 윈도우 서비스와 상호 작용할 때 도메인 기반 기능(예: Kerberos 인증)에 GMSA를 사용하도록 구성할 수 있다.
쿠버네티스 클러스터가 있어야 하며 클러스터와 통신하도록 kubectl
커맨드라인 툴을 구성해야 한다. 클러스터에는 윈도우 워커 노드가 있어야 한다. 이 섹션에서는 각 클러스터에 대해 한 번씩 필요한 일련의 초기 단계를 다룬다.
GMSA 자격 증명 사양 리소스에 대한 커스텀리소스데피니션(CustomResourceDefinition, CRD)을 클러스터에서 구성하여 사용자 정의 리소스 유형 GMSACredentialSpec
을 정의해야 한다. GMSA CRD YAML을 다운로드하고 gmsa-crd.yaml로 저장한다.
다음, kubectl apply -f gmsa-crd.yaml
로 CRD를 설치한다.
쿠버네티스 클러스터에서 두 개의 웹훅을 구성하여 파드 또는 컨테이너 수준에서 GMSA 자격 증명 사양 참조를 채우고 검증한다.
변형(mutating) 웹훅은 (파드 사양의 이름별로) GMSA에 대한 참조를 파드 사양 내 JSON 형식의 전체 자격 증명 사양으로 확장한다.
검증(validating) 웹훅은 GMSA에 대한 모든 참조가 파드 서비스 어카운트에서 사용하도록 승인되었는지 확인한다.
위의 웹훅 및 관련 오브젝트를 설치하려면 다음 단계가 필요하다.
인증서 키 쌍 생성 (웹훅 컨테이너가 클러스터와 통신할 수 있도록 하는데 사용됨)
위의 인증서로 시크릿을 설치
핵심 웹훅 로직에 대한 디플로이먼트(deployment)를 생성
디플로이먼트를 참조하여 검증 및 변경 웹훅 구성을 생성
스크립트를 사용하여 GMSA 웹훅과 위에서 언급한 관련 오브젝트를 배포 및 구성할 수 있다. 스크립트는 --dry-run=server
옵션으로 실행되어 클러스터에 대한 변경 사항을 검토할 수 있다.
스크립트에서 사용하는 YAML 템플릿을 사용하여 웹훅 및 (파라미터를 적절히 대체하여) 관련 오브젝트를 수동으로 배포할 수도 있다.
쿠버네티스의 파드가 GMSA를 사용하도록 구성되기 전에 윈도우 GMSA 문서에 설명된 대로 액티브 디렉터리에서 원하는 GMSA를 프로비저닝해야 한다. 윈도우 GMSA 문서에 설명된 대로 원하는 GMSA와 연결된 시크릿 자격 증명에 접근하려면 (쿠버네티스 클러스터의 일부인) 윈도우 워커 노드를 액티브 디렉터리에서 구성해야 한다.
(앞에서 설명한 대로) GMSACredentialSpec CRD를 설치하면 GMSA 자격 증명 사양이 포함된 사용자 정의 리소스를 구성할 수 있다. GMSA 자격 증명 사양에는 시크릿 또는 민감한 데이터가 포함되어 있지 않다. 이것은 컨테이너 런타임이 원하는 윈도우 컨테이너 GMSA를 설명하는 데 사용할 수 있는 정보이다. GMSA 자격 증명 사양은 PowerShell 스크립트 유틸리티를 사용하여 YAML 형식으로 생성할 수 있다.
다음은 JSON 형식으로 GMSA 자격 증명 사양 YAML을 수동으로 생성한 다음 변환하는 단계이다.
CredentialSpec 모듈 가져오기(import): ipmo CredentialSpec.psm1
New-CredentialSpec
을 사용하여 JSON 형식의 자격 증명 사양을 만든다. WebApp1이라는 GMSA 자격 증명 사양을 만들려면 New-CredentialSpec -Name WebApp1 -AccountName WebApp1 -Domain $(Get-ADDomain -Current LocalComputer)
를 호출한다.
Get-CredentialSpec
을 사용하여 JSON 파일의 경로를 표시한다.
credspec 파일을 JSON에서 YAML 형식으로 변환하고 필요한 헤더 필드 apiVersion
, kind
, metadata
, credspec
을 적용하여 쿠버네티스에서 구성할 수 있는 GMSACredentialSpec 사용자 정의 리소스로 만든다.
다음 YAML 구성은 gmsa-WebApp1
이라는 GMSA 자격 증명 사양을 설명한다.
apiVersion: windows.k8s.io/v1
kind: GMSACredentialSpec
metadata:
name: gmsa-WebApp1 #임의의 이름이지만 참조로 사용된다.
credspec:
ActiveDirectoryConfig:
GroupManagedServiceAccounts:
- Name: WebApp1 #GMSA 계정의 사용자 이름
Scope: CONTOSO #NETBIOS 도메인 명
- Name: WebApp1 #GMSA 계정의 사용자 이름
Scope: contoso.com #DNS 도메인 명
CmsPlugins:
- ActiveDirectory
DomainJoinConfig:
DnsName: contoso.com #DNS 도메인 명
DnsTreeName: contoso.com #DNS 도메인 명 루트
Guid: 244818ae-87ac-4fcd-92ec-e79e5252348a #GUID
MachineAccountName: WebApp1 #GMSA 계정의 사용자 이름
NetBiosName: CONTOSO #NETBIOS 도메인 명
Sid: S-1-5-21-2126449477-2524075714-3094792973 #SID of GMSA
위의 자격 증명 사양 리소스는 gmsa-Webapp1-credspec.yaml
로 저장되고 kubectl apply -f gmsa-Webapp1-credspec.yml
을 사용하여 클러스터에 적용될 수 있다.
각 GMSA 자격 증명 사양 리소스에 대해 cluster role을 정의해야 한다. 이것은 일반적으로 서비스 어카운트인 주체에 의해 특정 GMSA 리소스에 대한 use
동사를 승인한다. 다음 예는 위에서 gmsa-WebApp1
자격 증명 사양의 사용을 승인하는 클러스터 롤(cluster role)을 보여준다. 파일을 gmsa-webapp1-role.yaml로 저장하고 kubectl apply -f gmsa-webapp1-role.yaml
을 사용하여 적용한다.
#credspec을 읽을 Role 생성
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: webapp1-role
rules:
- apiGroups: ["windows.k8s.io"]
resources: ["gmsacredentialspecs"]
verbs: ["use"]
resourceNames: ["gmsa-WebApp1"]
(파드가 사용하게 되는) 서비스 어카운트는 위에서 생성한 클러스터 롤에 바인딩되어야 한다. 이렇게 하면 서비스 어카운트가 원하는 GMSA 자격 증명 사양 리소스를 사용할 수 있다. 다음은 위에서 생성한 gmsa-WebApp1
자격 증명 사양 리소스를 사용하기 위해 webapp1-role
클러스터 롤에 바인딩되는 기본(default) 서비스 어카운트이다.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: allow-default-svc-account-read-on-gmsa-WebApp1
namespace: default
subjects:
- kind: ServiceAccount
name: default
namespace: default
roleRef:
kind: ClusterRole
name: webapp1-role
apiGroup: rbac.authorization.k8s.io
파드 사양 필드 securityContext.windowsOptions.gmsaCredentialSpecName
은 파드 사양에서 원하는 GMSA 자격 증명 사양 사용자 정의 리소스에 대한 참조를 지정하는 데 사용된다. 이렇게 하면 지정된 GMSA를 사용하도록 파드 사양의 모든 컨테이너가 구성된다. 다음은 gmsa-WebApp1
을 참조하도록 채워진 어노테이션이 있는 샘플 파드 사양이다.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
run: with-creds
name: with-creds
namespace: default
spec:
replicas: 1
selector:
matchLabels:
run: with-creds
template:
metadata:
labels:
run: with-creds
spec:
securityContext:
windowsOptions:
gmsaCredentialSpecName: gmsa-webapp1
containers:
- image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
imagePullPolicy: Always
name: iis
nodeSelector:
kubernetes.io/os: windows
파드 사양의 개별 컨테이너는 컨테이너별 securityContext.windowsOptions.gmsaCredentialSpecName
필드를 사용하여 원하는 GMSA credspec을 지정할 수도 있다. 다음은 예이다.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
run: with-creds
name: with-creds
namespace: default
spec:
replicas: 1
selector:
matchLabels:
run: with-creds
template:
metadata:
labels:
run: with-creds
spec:
containers:
- image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
imagePullPolicy: Always
name: iis
securityContext:
windowsOptions:
gmsaCredentialSpecName: gmsa-Webapp1
nodeSelector:
kubernetes.io/os: windows
(위에서 설명한 대로) GMSA 필드가 채워진 파드 사양이 클러스터에 적용되면 다음과 같은 일련의 이벤트가 발생한다.
변형 웹훅은 GMSA 자격 증명 사양 리소스에 대한 모든 참조를 확인하고 GMSA 자격 증명 사양의 내용으로 확장한다.
검증 웹훅은 파드와 연결된 서비스 어카운트가 지정된 GMSA 자격 증명 사양의 use
동사에 대해 승인되었는지 확인한다.
컨테이너 런타임은 컨테이너가 액티브 디렉터리에서 GMSA의 ID를 가정하고 해당 ID를 사용하여 도메인의 서비스에 접근할 수 있도록 지정된 GMSA 자격 증명 사양으로 각 윈도우 컨테이너를 구성한다.
파드에서 호스트네임 또는 FQDN을 사용하여 SMB 공유에 연결할 때 문제를 겪고 있으나, IPv4 주소로는 해당 공유에 접속이 가능한 상황이라면, 윈도우 노드에 다음 레지스트리 키를 등록했는지 확인한다.
reg add "HKLM\SYSTEM\CurrentControlSet\Services\hns\State" /v EnableCompartmentNamespace /t REG_DWORD /d 1
그런 다음 동작 변경 사항을 적용하려면 실행 중인 파드를 다시 생성해야 한다. 이 레지스트리 키가 어떻게 사용되는지에 대한 자세한 정보는 여기에서 볼 수 있다.
GMSA가 사용자 환경에서 작동하도록 하는 데 어려움이 있는 경우 취할 수 있는 몇 가지 문제 해결 단계가 있다.
먼저 credspec이 파드에 전달되었는지 확인한다. 이렇게 하려면 파드 중 하나에서 exec
를 실행하고 nltest.exe /parentdomain
명령의 출력을 확인해야 한다.
아래 예에서 파드는 credspec을 올바르게 가져오지 못했다.
kubectl exec -it iis-auth-7776966999-n5nzr powershell.exe
nltest.exe /parentdomain
는 다음과 같은 오류를 발생시킨다.
Getting parent domain failed: Status = 1722 0x6ba RPC_S_SERVER_UNAVAILABLE
파드가 credspec을 올바르게 가져오면 다음으로 도메인과의 통신을 확인한다. 먼저 파드 내부에서 nslookup을 빠르게 수행하여 도메인의 루트를 찾는다.
이것은 다음의 세 가지를 의미한다.
DNS 및 통신 테스트를 통과하면 다음으로 파드가 도메인과 보안 채널 통신을 설정했는지 확인해야 한다. 이렇게 하려면 파드에서 다시 exec
를 실행하고 nltest.exe /query
명령을 실행한다.
nltest.exe /query
결과는 다음과 같다.
I_NetLogonControl failed: Status = 1722 0x6ba RPC_S_SERVER_UNAVAILABLE
이것은 어떤 이유로 파드가 credspec에 지정된 계정을 사용하여 도메인에 로그온할 수 없음을 알려준다. 다음을 실행하여 보안 채널 복구를 시도할 수 있다.
nltest /sc_reset:domain.example
명령이 성공하면 다음과 유사한 출력이 표시된다.
Flags: 30 HAS_IP HAS_TIMESERV
Trusted DC Name \\dc10.domain.example
Trusted DC Connection Status Status = 0 0x0 NERR_Success
The command completed successfully
위의 방법으로 오류가 수정되면 다음 수명 주기 훅(hook)을 파드 사양에 추가하여 단계를 자동화할 수 있다. 오류가 수정되지 않은 경우 credspec을 다시 검사하여 정확하고 완전한지 확인해야 한다.
image: registry.domain.example/iis-auth:1809v1
lifecycle:
postStart:
exec:
command: ["powershell.exe","-command","do { Restart-Service -Name netlogon } while ( $($Result = (nltest.exe /query); if ($Result -like '*0x0 NERR_Success*') {return $true} else {return $false}) -eq $false)"]
imagePullPolicy: IfNotPresent
위의 lifecycle
섹션을 파드 사양에 추가하면, 파드는 nltest.exe /query
명령이 오류 없이 종료될 때까지 나열된 명령을 실행하여 netlogon
서비스를 다시 시작한다.
이 페이지에서는 컨테이너의 CPU 요청량과 CPU 상한을 지정하는 방법을 보여준다. 컨테이너는 설정된 상한보다 더 많은 CPU는 사용할 수 없다. 제공된 시스템에 CPU 가용량이 남아있다면, 컨테이너는 요청량만큼의 CPU를 할당받는 것을 보장받는다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
태스크 예제를 수행하기 위해서는 최소 1개의 CPU가 가용한 클러스터가 필요하다.
이 페이지의 몇 가지 단계를 수행하기 위해서는 클러스터 내 metrics-server 서비스 실행이 필요하다. 이미 실행 중인 metrics-server가 있다면 다음 단계를 건너뛸 수 있다.
Minikube를 사용 중이라면, 다음 명령어를 실행해 metric-server를 활성화할 수 있다.
minikube addons enable metrics-server
metric-server(아니면 metrics.k8s.io
와 같은 다른 제공자의 리소스 메트릭 API)가
실행 중인지를 확인하기 위해 다음의 명령어를 실행한다.
kubectl get apiservices
리소스 메트릭 API를 사용할 수 있다면 출력에 metrics.k8s.io
에
대한 참조가 포함되어 있을 것이다.
NAME
v1beta1.metrics.k8s.io
이 예제에서 생성한 자원과 클러스터 내 나머지를 분리하기 위해 네임스페이스(Namespace)를 생성한다.
kubectl create namespace cpu-example
컨테이너에 CPU 요청량을 지정하기 위해서는 컨테이너의 리소스 매니페스트에 resources:requests
필드를 포함한다. CPU 상한을 지정하기 위해서는 resources:limits
필드를 포함한다.
이 예제에서는, 하나의 컨테이너를 가진 파드를 생성한다. 컨테이너는 0.5 CPU 요청량과 1 CPU 상한을 갖는다. 아래는 파드의 구성 파일이다.
apiVersion: v1
kind: Pod
metadata:
name: cpu-demo
namespace: cpu-example
spec:
containers:
- name: cpu-demo-ctr
image: vish/stress
resources:
limits:
cpu: "1"
requests:
cpu: "0.5"
args:
- -cpus
- "2"
구성 파일 내 args
섹션은 컨테이너가 시작될 때 인수(argument)를 제공한다.
-cpus "2"
인수는 컨테이너가 2 CPU 할당을 시도하도록 한다.
파드 생성:
kubectl apply -f https://k8s.io/examples/pods/resource/cpu-request-limit.yaml --namespace=cpu-example
파드가 실행 중인지 확인:
kubectl get pod cpu-demo --namespace=cpu-example
파드에 대한 자세한 정보 확인:
kubectl get pod cpu-demo --output=yaml --namespace=cpu-example
출력은 파드 내 하나의 컨테이너에 0.5 milliCPU 요청량과 1 CPU 상한이 있는 것을 보여준다.
resources:
limits:
cpu: "1"
requests:
cpu: 500m
kubectl top
을 실행하여 파드 메트릭 가져오기:
kubectl top pod cpu-demo --namespace=cpu-example
출력은 파드가 974 milliCPU를 사용하는 것을 보여주는데, 이는 파드의 1 CPU 상한보다는 약간 적은 수치이다.
NAME CPU(cores) MEMORY(bytes)
cpu-demo 974m <something>
만약 -cpu "2"
로 설정한다면, 컨테이너가 2 CPU를 사용하도록 설정한 것이 된다. 하지만 컨테이너는 1 CPU까지만을 사용하도록 허용되어 있다는 사실을 기억하자. 컨테이너는 상한보다 더 많은 CPU 리소스를 사용하려고 하기 때문에, 컨테이너의 CPU 사용은 쓰로틀(throttled) 될 것이다.
CPU 리소스는 CPU 단위로 측정된다. 쿠버네티스에서 1 CPU는, 다음과 같다.
분수 값도 가능하다. 0.5 CPU를 요청한 컨테이너는 1 CPU를 요청한 컨테이너 CPU의 절반 가량을 보장받는다. 접미사 m을 사용하여 밀리(milli)를 표현할 수도 있다. 예를 들어서 100m CPU, 100milliCPU, 그리고 0.1 CPU는 모두 같다. 1m보다 정밀한 표현은 허용하지 않는다.
CPU는 상대적인 수량이 아닌, 절대적인 수량으로 요청된다. 즉 0.1는 싱글 코어, 듀얼 코어, 48-코어 머신에서도 같은 양을 나타낸다.
파드 삭제:
kubectl delete pod cpu-demo --namespace=cpu-example
CPU 요청량과 상한은 컨테이너와 연관되어 있지만, 파드가 CPU 요청량과 상한을 갖는다고 생각하는 것이 유용하다. 특정 파드의 CPU 요청량은 해당 파드의 모든 컨테이너 CPU 요청량의 합과 같다. 마찬가지로, 특정 파드의 CPU 상한은 해당 파드의 모든 컨테이너 CPU 상한의 합과 같다.
파드 스케줄링은 요청량에 따라 수행된다. 파드는 파드 CPU 요청량을 만족할 정도로 노드에 충분한 CPU 리소스가 있을 때에만 노드에 스케줄링한다.
이 예제에서는, 클러스터의 모든 노드 가용량을 초과하는 CPU 요청량을 가진 파드를 생성했다. 아래는 하나의 컨테이너를 가진 파드에 대한 설정 파일이다. 컨테이너는 100 CPU을 요청하고 있는데, 이것은 클러스터의 모든 노드 가용량을 초과하는 것이다.
apiVersion: v1
kind: Pod
metadata:
name: cpu-demo-2
namespace: cpu-example
spec:
containers:
- name: cpu-demo-ctr-2
image: vish/stress
resources:
limits:
cpu: "100"
requests:
cpu: "100"
args:
- -cpus
- "2"
파드 생성:
kubectl apply -f https://k8s.io/examples/pods/resource/cpu-request-limit-2.yaml --namespace=cpu-example
파드 상태 확인:
kubectl get pod cpu-demo-2 --namespace=cpu-example
출력은 파드 상태가 Pending 상태임을 보여준다. 이것은 파드는 어떤 노드에서도 실행되도록 스케줄되지 않았고, 이후에도 Pending 상태가 지속될 것이라는 것을 의미한다.
NAME READY STATUS RESTARTS AGE
cpu-demo-2 0/1 Pending 0 7m
이벤트를 포함한 파드 상세 정보 확인:
kubectl describe pod cpu-demo-2 --namespace=cpu-example
출력은 노드의 CPU 리소스가 부족하여 파드가 스케줄링될 수 없음을 보여준다.
Events:
Reason Message
------ -------
FailedScheduling No nodes are available that match all of the following predicates:: Insufficient cpu (3).
파드 삭제:
kubectl delete pod cpu-demo-2 --namespace=cpu-example
컨테이너에 CPU 상한을 지정하지 않으면 다음 상황 중 하나가 발생한다.
컨테이너가 사용할 수 있는 CPU 리소스에 상한선이 없다. 컨테이너는 실행 중인 노드에서 사용 가능한 모든 CPU 리소스를 사용해버릴 수도 있다.
기본 CPU 상한이 지정된 네임스페이스에서 실행 중인 컨테이너에는 해당 기본 상한이 자동으로 할당된다. 클러스터 관리자들은 리밋레인지(LimitRange)를 사용해 CPU 상한의 기본 값을 지정할 수 있다.
만약 CPU 상한은 지정했지만 CPU 요청량을 지정하지 않았다면, 쿠버네티스는 자동으로 상한에 맞는 CPU 요청량을 지정한다. 비슷하게, 컨테이너가 자신의 메모리 상한을 지정했지만 메모리 요청량을 지정하지 않았다면, 쿠버네티스는 자동으로 상한에 맞는 메모리 요청량을 지정한다.
클러스터에서 실행되는 컨테이너에 CPU 요청량과 상한을 구성하면 클러스터 내 노드들의 가용 가능한 CPU 리소스를 효율적으로 사용할 수 있게 된다. 파드의 CPU 요청량을 낮게 유지하면 파드가 높은 확률로 스케줄링 될 수 있다. CPU 상한이 CPU 요청량보다 크도록 설정한다면 다음 두 가지를 달성할 수 있다.
네임스페이스 삭제:
kubectl delete namespace cpu-example
이 페이지는 특정 서비스 품질(QoS) 클래스를 할당하기 위해 어떻게 파드를 구성해야 하는지 보여준다. 쿠버네티스는 QoS 클래스를 사용하여 파드 스케줄링과 축출을 결정한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
쿠버네티스가 파드를 생성할 때, 파드에 다음의 QoS 클래스 중 하나를 할당한다.
이 연습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.
kubectl create namespace qos-example
파드에 Guaranteed QoS 클래스 할당을 위한 전제 조건은 다음과 같다.
이러한 제약은 초기화 컨테이너와 앱 컨테이너 모두에 동일하게 적용된다.
다음은 하나의 컨테이너를 갖는 파드의 구성 파일이다. 해당 컨테이너는 메모리 상한과 메모리 요청량을 갖고 있고, 200MiB로 동일하다. 해당 컨테이너는 CPU 상한과 CPU 요청량을 가지며, 700 milliCPU로 동일하다.
apiVersion: v1
kind: Pod
metadata:
name: qos-demo
namespace: qos-example
spec:
containers:
- name: qos-demo-ctr
image: nginx
resources:
limits:
memory: "200Mi"
cpu: "700m"
requests:
memory: "200Mi"
cpu: "700m"
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod.yaml --namespace=qos-example
파드의 상세 정보를 본다.
kubectl get pod qos-demo --namespace=qos-example --output=yaml
출력 결과는 쿠버네티스가 파드에 Guaranteed QoS 클래스를 부여했음을 보여준다. 또한 파드의 컨테이너가 메모리 요청량과 일치하는 메모리 상한을 가지며, CPU 요청량과 일치하는 CPU 상한을 갖고 있음을 확인할 수 있다.
spec:
containers:
...
resources:
limits:
cpu: 700m
memory: 200Mi
requests:
cpu: 700m
memory: 200Mi
...
status:
qosClass: Guaranteed
파드를 삭제한다.
kubectl delete pod qos-demo --namespace=qos-example
다음의 경우 파드에 Burstable QoS 클래스가 부여된다.
컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너는 200MiB의 메모리 상한과 100MiB의 메모리 요청량을 가진다.
apiVersion: v1
kind: Pod
metadata:
name: qos-demo-2
namespace: qos-example
spec:
containers:
- name: qos-demo-2-ctr
image: nginx
resources:
limits:
memory: "200Mi"
requests:
memory: "100Mi"
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod-2.yaml --namespace=qos-example
파드의 상세 정보를 본다.
kubectl get pod qos-demo-2 --namespace=qos-example --output=yaml
출력 결과는 쿠버네티스가 파드에 Burstable QoS 클래스를 부여했음을 보여준다.
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: qos-demo-2-ctr
resources:
limits:
memory: 200Mi
requests:
memory: 100Mi
...
status:
qosClass: Burstable
파드를 삭제한다.
kubectl delete pod qos-demo-2 --namespace=qos-example
파드에 BestEffort QoS 클래스를 부여하려면, 파드의 컨테이너에 메모리와 CPU에 대한 상한이나 요청량이 없어야 한다.
다음은 컨테이너가 하나인 파드의 구성 파일이다. 해당 컨테이너는 메모리와 CPU의 상한이나 요청량을 갖지 않는다.
apiVersion: v1
kind: Pod
metadata:
name: qos-demo-3
namespace: qos-example
spec:
containers:
- name: qos-demo-3-ctr
image: nginx
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod-3.yaml --namespace=qos-example
파드의 상세 정보를 본다.
kubectl get pod qos-demo-3 --namespace=qos-example --output=yaml
출력 결과는 쿠버네티스가 파드에 BestEffort QoS 클래스를 부여했음을 보여준다.
spec:
containers:
...
resources: {}
...
status:
qosClass: BestEffort
파드를 삭제한다.
kubectl delete pod qos-demo-3 --namespace=qos-example
컨테이너가 두 개인 파드의 구성 파일이다. 한 컨테이너는 200MiB의 메모리 요청량을 지정한다. 다른 컨테이너는 어떤 요청량이나 상한을 지정하지 않는다.
apiVersion: v1
kind: Pod
metadata:
name: qos-demo-4
namespace: qos-example
spec:
containers:
- name: qos-demo-4-ctr-1
image: nginx
resources:
requests:
memory: "200Mi"
- name: qos-demo-4-ctr-2
image: redis
참고로 이 파드는 Burstable QoS 클래스의 기준을 충족한다. 즉, Guaranteed QoS 클래스에 대한 기준을 충족하지 않으며, 해당 컨테이너 중 하나가 메모리 요청량을 갖는다.
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod-4.yaml --namespace=qos-example
파드의 상세 정보를 본다.
kubectl get pod qos-demo-4 --namespace=qos-example --output=yaml
출력 결과는 쿠버네티스가 파드에 Burstable QoS 클래스를 부여했음을 보여준다.
spec:
containers:
...
name: qos-demo-4-ctr-1
resources:
requests:
memory: 200Mi
...
name: qos-demo-4-ctr-2
resources: {}
...
status:
qosClass: Burstable
파드를 삭제한다.
kubectl delete pod qos-demo-4 --namespace=qos-example
네임스페이스를 삭제한다.
kubectl delete namespace qos-example
Kubernetes v1.31 [stable]
이 페이지는 컨테이너에 확장 리소스를 지정하는 방법을 보여준다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
이 태스크를 수행하기 전에 노드에 대한 확장 리소스 알리기에서 연습한다. 그러면 노드 중 하나가 동글(dongle) 리소스를 알리도록 구성될 것이다.
확장 리소스를 요청하려면 컨테이너 매니페스트에 resources:requests
필드를 포함한다.
확장 리소스는 *.kubernetes.io/
외부의 모든 도메인으로 정규화된다.
유효한 확장 리소스 이름은 example.com/foo
형식을 갖는다.
여기서 example.com
은 조직의 도메인으로 대체하고,
foo
는 리소스를 설명할 수 있는 이름으로 짓는다.
다음은 컨테이너가 하나 있는 파드의 구성 파일이다.
apiVersion: v1
kind: Pod
metadata:
name: extended-resource-demo
spec:
containers:
- name: extended-resource-demo-ctr
image: nginx
resources:
requests:
example.com/dongle: 3
limits:
example.com/dongle: 3
구성 파일에서 컨테이너가 3개의 동글을 요청하는 것을 알 수 있다.
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/resource/extended-resource-pod.yaml
파드가 실행 중인지 확인한다.
kubectl get pod extended-resource-demo
파드의 상세 정보를 확인한다.
kubectl describe pod extended-resource-demo
출력은 동글 요청을 보여준다.
Limits:
example.com/dongle: 3
Requests:
example.com/dongle: 3
다음은 컨테이너가 하나 있는 파드의 구성 파일이다. 컨테이너는 두 개의 동글을 요청한다.
apiVersion: v1
kind: Pod
metadata:
name: extended-resource-demo-2
spec:
containers:
- name: extended-resource-demo-2-ctr
image: nginx
resources:
requests:
example.com/dongle: 2
limits:
example.com/dongle: 2
첫 번째 파드가 사용 가능한 4개의 동글 중 3개를 사용했기 때문에 쿠버네티스는 두 개의 동글에 대한 요청을 충족시킬 수 없을 것이다.
파드 생성을 시도한다.
kubectl apply -f https://k8s.io/examples/pods/resource/extended-resource-pod-2.yaml
파드 상세 정보를 확인한다.
kubectl describe pod extended-resource-demo-2
출력은 두 개의 동글을 가용할 수 있는 노드가 없기 때문에 파드를 스케줄할 수 없음을 보여준다.
Conditions:
Type Status
PodScheduled False
...
Events:
...
... Warning FailedScheduling pod (extended-resource-demo-2) failed to fit in any node
fit failure summary on nodes : Insufficient example.com/dongle (1)
파드 상태를 확인한다.
kubectl get pod extended-resource-demo-2
출력은 파드가 생성됐지만 노드에서 실행되도록 스케줄되지 않았음을 보여준다. 파드는 Pending 상태이다.
NAME READY STATUS RESTARTS AGE
extended-resource-demo-2 0/1 Pending 0 6m
연습을 위해 생성한 파드를 삭제한다.
kubectl delete pod extended-resource-demo
kubectl delete pod extended-resource-demo-2
이 페이지는 스토리지의 볼륨을 사용하는 파드를 구성하는 방법을 설명한다.
컨테이너 파일 시스템은 컨테이너가 살아있는 동안만 존재한다. 따라서 컨테이너가 종료되고 재시작할 때, 파일 시스템 변경사항이 손실된다. 컨테이너와 독립적이며 보다 일관된 스토리지를 위해 사용자는 볼륨을 사용할 수 있다. 이것은 레디스(Redis)와 같은 키-값 저장소나 데이터베이스와 같은 스테이트풀 애플리케이션에 매우 중요하다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
이 연습에서는 하나의 컨테이너를 실행하는 파드를 생성한다. 이 파드는 컨테이너가 종료되고, 재시작 하더라도 파드의 수명동안 지속되는 emptyDir 유형의 볼륨이 있다. 파드의 구성 파일은 다음과 같다.
apiVersion: v1
kind: Pod
metadata:
name: redis
spec:
containers:
- name: redis
image: redis
volumeMounts:
- name: redis-storage
mountPath: /data/redis
volumes:
- name: redis-storage
emptyDir: {}
파드 생성
kubectl apply -f https://k8s.io/examples/pods/storage/redis.yaml
파드의 컨테이너가 Running 중인지 확인하고, 파드의 변경사항을 지켜본다.
kubectl get pod redis --watch
출력은 이와 유사하다.
NAME READY STATUS RESTARTS AGE
redis 1/1 Running 0 13s
다른 터미널에서 실행 중인 컨테이너의 셸을 획득한다.
kubectl exec -it redis -- /bin/bash
셸에서 /data/redis
로 이동하고, 파일을 생성한다.
root@redis:/data# cd /data/redis/
root@redis:/data/redis# echo Hello > test-file
셸에서 실행 중인 프로세스 목록을 확인한다.
root@redis:/data/redis# apt-get update
root@redis:/data/redis# apt-get install procps
root@redis:/data/redis# ps aux
출력은 이와 유사하다.
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
redis 1 0.1 0.1 33308 3828 ? Ssl 00:46 0:00 redis-server *:6379
root 12 0.0 0.0 20228 3020 ? Ss 00:47 0:00 /bin/bash
root 15 0.0 0.0 17500 2072 ? R+ 00:48 0:00 ps aux
셸에서 Redis 프로세스를 강제종료(kill)한다.
root@redis:/data/redis# kill <pid>
여기서 <pid>
는 Redis 프로세스 ID(PID) 이다.
원래 터미널에서, Redis 파드의 변경을 지켜본다. 결국, 다음과 유사한 것을 보게 될 것이다.
NAME READY STATUS RESTARTS AGE
redis 1/1 Running 0 13s
redis 0/1 Completed 0 6m
redis 1/1 Running 1 6m
이때, 컨테이너는 종료되고 재시작된다. 이는
Redis 파드의
restartPolicy는
Always
이기 때문이다.
재시작된 컨테이너의 셸을 획득한다.
kubectl exec -it redis -- /bin/bash
셸에서 /data/redis
로 이동하고, test-file
이 여전히 존재하는지 확인한다.
root@redis:/data/redis# cd /data/redis/
root@redis:/data/redis# ls
test-file
이 연습을 위해 생성한 파드를 삭제한다.
kubectl delete pod redis
이 페이지는 스토리지에 대해 퍼시스턴트볼륨클레임(PersistentVolumeClaim)을 사용하도록 파드를 설정하는 방법을 보여준다. 과정의 요약은 다음과 같다.
클러스터 관리자로서, 물리적 스토리지와 연결되는 퍼시스턴트볼륨을 생성한다. 볼륨을 특정 파드와 연결하지 않는다.
그 다음 개발자 / 클러스터 사용자의 역할로서, 적합한 퍼시스턴트볼륨에 자동으로 바인딩되는 퍼시스턴트볼륨클레임을 생성한다.
스토리지에 대해 위의 퍼시스턴트볼륨클레임을 사용하는 파드를 생성한다.
사용자는 노드가 단 하나만 있는 쿠버네티스 클러스터가 필요하고, kubectl 커맨드라인 툴이 사용자의 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 만약 사용자가 아직 단일 노드 클러스터를 가지고 있지 않다면, Minikube를 사용하여 클러스터 하나를 생성할 수 있다.
퍼시스턴트 볼륨의 관련 자료에 익숙해지도록 한다.
사용자 클러스터의 단일 노드에 연결되는 셸을 연다. 셸을 여는 방법은
클러스터 설정에 따라 달라진다. 예를 들어 Minikube를 사용하는 경우,
minikube ssh
명령어를 입력하여 노드로 연결되는 셸을 열 수 있다.
해당 노드의 셸에서 /mnt/data
디렉터리를 생성한다.
# 사용자 노드에서 슈퍼유저로 명령을 수행하기 위하여
# "sudo"를 사용한다고 가정한다
sudo mkdir /mnt/data
/mnt/data
디렉터리에서 index.html
파일을 생성한다.
# 이번에도 사용자 노드에서 슈퍼유저로 명령을 수행하기 위하여
# "sudo"를 사용한다고 가정한다
sudo sh -c "echo 'Hello from Kubernetes storage' > /mnt/data/index.html"
sudo
이외의 슈퍼유저 접근 툴을 사용하는 경우,
sudo
를 해당 툴의 이름으로 바꾸면, 동일하게 작업을 수행할 수 있다.index.html
파일이 존재하는지 테스트한다.
cat /mnt/data/index.html
결과는 다음과 같다.
Hello from Kubernetes storage
이제 사용자 노드에서 셸을 종료해도 된다.
이 예제에서, 사용자는 hostPath 퍼시스턴트볼륨을 생성한다. 쿠버네티스는 단일 노드에서의 개발과 테스트를 위해 hostPath를 지원한다. hostPath 퍼시스턴트볼륨은 네트워크로 연결된 스토리지를 모방하기 위해, 노드의 파일이나 디렉터리를 사용한다.
운영 클러스터에서, 사용자가 hostPath를 사용하지는 않는다. 대신, 클러스터 관리자는 Google Compute Engine 영구 디스크, NFS 공유 또는 Amazone Elastic Block Store 볼륨과 같은 네트워크 자원을 프로비저닝한다. 클러스터 관리자는 스토리지클래스(StorageClasses)를 사용하여 동적 프로비저닝을 설정할 수도 있다.
hostPath 퍼시스턴트볼륨의 설정 파일은 아래와 같다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
설정 파일에 클러스터 노드의 /mnt/data
에 볼륨이 있다고
지정한다. 또한 설정에서 볼륨 크기를 10 기가바이트로 지정하고 단일 노드가
읽기-쓰기 모드로 볼륨을 마운트할 수 있는 ReadWriteOnce
접근 모드를 지정한다. 여기서는
퍼시스턴트볼륨클레임의 스토리지클래스 이름을
manual
로 정의하며, 퍼시스턴트볼륨클레임의 요청을
이 퍼시스턴트볼륨에 바인딩하는 데 사용한다.
퍼시스턴트볼륨을 생성한다.
kubectl apply -f https://k8s.io/examples/pods/storage/pv-volume.yaml
퍼시스턴트볼륨에 대한 정보를 조회한다.
kubectl get pv task-pv-volume
결과는 퍼시스턴트볼륨의 STATUS
가 Available
임을 보여준다. 이는
아직 퍼시스턴트볼륨클레임이 바인딩되지 않았다는 것을 의미한다.
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM STORAGECLASS REASON AGE
task-pv-volume 10Gi RWO Retain Available manual 4s
다음 단계는 퍼시스턴트볼륨클레임을 생성하는 단계이다. 파드는 퍼시스턴트볼륨클레임을 사용하여 물리적인 스토리지를 요청한다. 이 예제에서, 사용자는 적어도 하나 이상의 노드에 대해 읽기-쓰기 접근을 지원하며 최소 3 기가바이트의 볼륨을 요청하는 퍼시스턴트볼륨클레임을 생성한다.
퍼시스턴트볼륨클레임에 대한 설정 파일은 다음과 같다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
퍼시스턴트볼륨클레임을 생성한다.
kubectl apply -f https://k8s.io/examples/pods/storage/pv-claim.yaml
사용자가 퍼시스턴트볼륨클레임을 생성한 후에, 쿠버네티스 컨트롤 플레인은 클레임의 요구사항을 만족하는 퍼시스턴트볼륨을 찾는다. 컨트롤 플레인이 동일한 스토리지클래스를 갖는 적절한 퍼시스턴트볼륨을 찾으면, 볼륨에 클레임을 바인딩한다.
퍼시스턴트볼륨을 다시 확인한다.
kubectl get pv task-pv-volume
이제 결과는 STATUS
가 Bound
임을 보여준다.
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM STORAGECLASS REASON AGE
task-pv-volume 10Gi RWO Retain Bound default/task-pv-claim manual 2m
퍼시스턴트볼륨클레임을 확인한다.
kubectl get pvc task-pv-claim
결과는 퍼시스턴트볼륨클레임이 사용자의 퍼시스턴트볼륨인 task-pv-volume
에
바인딩되어 있음을 보여준다.
NAME STATUS VOLUME CAPACITY ACCESSMODES STORAGECLASS AGE
task-pv-claim Bound task-pv-volume 10Gi RWO manual 30s
다음 단계는 볼륨으로 퍼시스턴트볼륨클레임을 사용하는 파드를 만드는 단계이다.
파드에 대한 설정 파일은 다음과 같다.
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: task-pv-claim
containers:
- name: task-pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: task-pv-storage
파드의 설정 파일은 퍼시스턴트볼륨클레임을 지정하지만, 퍼시스턴트볼륨을 지정하지는 않는다는 것을 유념하자. 파드의 관점에서 볼때, 클레임은 볼륨이다.
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/storage/pv-pod.yaml
파드의 컨테이너가 실행 중임을 확인한다.
kubectl get pod task-pv-pod
사용자 파드에서 구동되고 있는 컨테이너에 셸로 접근한다.
kubectl exec -it task-pv-pod -- /bin/bash
사용자의 셸에서, nginx가 hostPath 볼륨으로부터 index.html
파일을
제공하는지 확인한다.
# 이전 단계에서 "kubectl exec" 명령을 실행한 root 셸 안에서
# 다음의 3개 명령을 실행해야 한다.
apt update
apt install curl
curl http://localhost/
결과는 hostPath 볼륨에 있는 index.html
파일에 사용자가 작성한 텍스트를
보여준다.
Hello from Kubernetes storage
만약 사용자가 위와 같은 메시지를 확인하면, 파드가 퍼시스턴트볼륨클레임의 스토리지를 사용하도록 성공적으로 설정한 것이다.
파드, 퍼시스턴트볼륨클레임, 퍼시스턴트볼륨을 삭제한다.
kubectl delete pod task-pv-pod
kubectl delete pvc task-pv-claim
kubectl delete pv task-pv-volume
만약 클러스터의 노드에 대한 셸이 열려져 있지 않은 경우, 이전과 동일한 방식으로 새로운 셸을 연다.
사용자 노드의 셸에서, 생성한 파일과 디렉터리를 제거한다.
# 사용자 노드에서 슈퍼유저로 명령을 수행하기 위하여
# "sudo"를 사용한다고 가정한다
sudo rm /mnt/data/index.html
sudo rmdir /mnt/data
이제 사용자 노드에서 셸을 종료해도 된다.
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: test
image: nginx
volumeMounts:
# a mount for site-data
- name: config
mountPath: /usr/share/nginx/html
subPath: html
# another mount for nginx config
- name: config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
volumes:
- name: config
persistentVolumeClaim:
claimName: test-nfs-claim
하나의 퍼시스턴트볼륨을 nginx 컨테이너의 두 경로에 마운트할 수 있다.
/usr/share/nginx/html
- 정적 웹사이트 용
/etc/nginx/nginx.conf
- 기본 환경 설정 용
그룹 ID(GID)로 설정된 스토리지는 동일한 GID를 사용하는 파드에서만 쓰기 작업을 허용한다. GID가 일치하지 않거나 누락되었을 경우 권한 거부 오류가 발생한다. 사용자와의 조정 필요성을 줄이기 위하여 관리자는 퍼시스턴트 볼륨에 GID로 어노테이션을 달 수 있다. 그 뒤에, 퍼시스턴트볼륨을 사용하는 모든 파드에 대하여 GID가 자동으로 추가된다.
다음과 같이 pv.beta.kubernetes.io/gid
어노테이션을 사용한다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
annotations:
pv.beta.kubernetes.io/gid: "1234"
파드가 GID 어노테이션이 있는 퍼시스턴트볼륨을 사용하면, 어노테이션으로 달린 GID가 파드의 보안 컨텍스트에 지정된 GID와 동일한 방식으로 파드의 모든 컨테이너에 적용된다. 파드의 명세 혹은 퍼시스턴트볼륨의 어노테이션으로부터 생성된 모든 GID는, 각 컨테이너에서 실행되는 첫 번째 프로세스에 적용된다.
이 페이지는 프로젝티드
볼륨을 사용하여 여러 기존 볼륨 소스들을
동일한 디렉터리에 마운트하는 방법을 보여준다. 현재 시크릿(secret)
, 컨피그맵(configMap)
, downwardAPI
,
그리고 서비스어카운트토큰(serviceAccountToken)
볼륨이 프로젝티드(projected)될 수 있다.
서비스어카운트토큰(serviceAccountToken)
은 볼륨 타입이 아니다.쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
이 연습에서는 로컬 파일에 유저네임과 패스워드를 시크릿으로 생성한다. 이후 하나의 컨테이너를 포함한 파드를 생성하는 데, 이 때 시크릿을 동일한 공유 디렉터리에 마운트하기 위해 프로젝티드
볼륨을 사용한다.
다음은 파드의 구성 파일이다.
apiVersion: v1
kind: Pod
metadata:
name: test-projected-volume
spec:
containers:
- name: test-projected-volume
image: busybox:1.28
args:
- sleep
- "86400"
volumeMounts:
- name: all-in-one
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: all-in-one
projected:
sources:
- secret:
name: user
- secret:
name: pass
시크릿을 생성한다.
# 유저네임과 패스워드를 포함한 파일들을 생성한다.
echo -n "admin" > ./username.txt
echo -n "1f2d1e2e67df" > ./password.txt
# 생성한 파일들을 시크릿으로 패키징한다.
kubectl create secret generic user --from-file=./username.txt
kubectl create secret generic pass --from-file=./password.txt
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/storage/projected.yaml
파드의 컨테이너가 정상적으로 실행되는지 확인한 다음, 파드에 대한 변경 사항을 확인한다.
kubectl get --watch pod test-projected-volume
The output looks like this:
NAME READY STATUS RESTARTS AGE
test-projected-volume 1/1 Running 0 14s
다른 터미널을 이용해, 실행 중인 컨테이너에 대한 셸을 가져온다.
kubectl exec -it test-projected-volume -- /bin/sh
셸에서 projected-volume
디렉터리에 프로젝티드 소스들이 포함되어 있는지 확인한다.
ls /projected-volume/
파드와 시크릿을 제거한다.
kubectl delete pod test-projected-volume
kubectl delete secret user pass
프로젝티드
볼륨에 대해 더 알아보기.이 페이지는 프라이빗 컨테이너 레지스트리나 리포지터리로부터 이미지를 받아오기 위해 시크릿(Secret)을 사용하는 파드를 생성하는 방법을 보여준다. 현재 많은 곳에서 프라이빗 레지스트리가 사용되고 있다. 여기서는 예시 레지스트리로 Docker Hub을 사용한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
이 실습을 수행하기 위해, docker
명령줄 도구와
도커 ID 및 비밀번호가 필요하다.
다른 프라이빗 컨테이너 레지스트리를 사용하는 경우, 해당 레지스트리를 위한 명령줄 도구 및 레지스트리 로그인 정보가 필요하다.
노트북에 프라이빗 이미지를 받아오기 위하여 레지스트리 인증을 필수로 수행해야 한다.
docker
도구를 사용하여 도커 허브에 로그인한다. 자세한 정보는
도커 ID 계정의 로그 인 섹션을 참조한다.
docker login
프롬프트가 나타나면, 도커 ID를 입력한 다음, 사용하려는 자격증명(액세스 토큰, 또는 도커 ID의 비밀번호)을 입력한다.
로그인 프로세스를 수행하면 권한 토큰 정보를 가지고 있는 config.json
파일이 생성되거나 업데이트된다. 쿠버네티스가 이 파일을 어떻게 해석하는지 참고한다.
config.json
파일을 확인하자.
cat ~/.docker/config.json
하단과 유사한 결과를 확인할 수 있다.
{
"auths": {
"https://index.docker.io/v1/": {
"auth": "c3R...zE2"
}
}
}
auth
항목이 아닌, 저장소의 이름을 값으로 사용하는 credsStore
항목을 확인할 수 있다.
이 경우, 시크릿을 직접 생성할 수 있다. 커맨드 라인에서 자격 증명을 통하여 시크릿 생성하기를 보자.쿠버네티스 클러스터는 프라이빗 이미지를 받아올 때, 컨테이너 레지스트리에 인증하기 위하여
kubernetes.io/dockerconfigjson
타입의 시크릿을 사용한다.
만약 이미 docker login
을 수행하였다면,
이 때 생성된 자격 증명을 쿠버네티스 클러스터로 복사할 수 있다.
kubectl create secret generic regcred \
--from-file=.dockerconfigjson=<path/to/.docker/config.json> \
--type=kubernetes.io/dockerconfigjson
오브젝트에 대한 더 세밀한 제어(새로운 시크릿에 대한 네임스페이스나 레이블을 지정하는 등)가 필요할 경우, 시크릿을 사용자 정의한 후에 저장할 수도 있다. 다음을 확인하자.
.dockerconfigjson
으로 설정한다data[".dockerconfigjson"]
필드에 자르지 않고 한 줄로 이어서 붙여넣는다type
을 kubernetes.io/dockerconfigjson
으로 설정한다예:
apiVersion: v1
kind: Secret
metadata:
name: myregistrykey
namespace: awesomeapps
data:
.dockerconfigjson: UmVhbGx5IHJlYWxseSByZWVlZWVlZWVlZWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGx5eXl5eXl5eXl5eXl5eXl5eXl5eSBsbGxsbGxsbGxsbGxsbG9vb29vb29vb29vb29vb29vb29vb29vb29vb25ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubmdnZ2dnZ2dnZ2dnZ2dnZ2dnZ2cgYXV0aCBrZXlzCg==
type: kubernetes.io/dockerconfigjson
만약 error: no objects passed to create
메세지가 출력될 경우, base64로 인코딩된 문자열이 유효하지 않음을 의미한다.
또한 Secret "myregistrykey" is invalid: data[.dockerconfigjson]: invalid value ...
메세지가 출력될 경우,
base64로 인코딩된 문자열이 정상적으로 디코딩되었으나, .docker/config.json
파일로 파싱되지 못한 것을 의미한다.
regcred
라는 이름의 시크릿을 생성하자.
kubectl create secret docker-registry regcred --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>
아래의 각 항목에 대한 설명을 참고한다.
<your-registry-server>
은 프라이빗 도커 저장소의 FQDN 주소이다.
도커허브(DockerHub)는 https://index.docker.io/v1/
를 사용한다.<your-name>
은 도커 사용자의 계정이다.<your-pword>
은 도커 사용자의 비밀번호이다.<your-email>
은 도커 사용자의 이메일 주소이다.이를 통해 regcred
라는 시크릿으로 클러스터 내에서 도커 자격 증명을 생성했다.
kubectl
이 구동 중인 동안 사용자의 PC의 다른 사용자들에게
보일 수도 있다.regcred
검증하기방금 생성한 regcred
시크릿의 내용을 확인하기 위하여, YAML 형식으로 시크릿을 확인하자.
kubectl get secret regcred --output=yaml
결과는 다음과 같다.
apiVersion: v1
kind: Secret
metadata:
...
name: regcred
...
data:
.dockerconfigjson: eyJodHRwczovL2luZGV4L ... J0QUl6RTIifX0=
type: kubernetes.io/dockerconfigjson
.dockerconfigjson
필드의 값은 도커 자격 증명의 base64 인코딩 결과이다.
.dockerconfigjson
필드의 값을 확인하기 위하여, 시크릿 데이터를 읽을 수 있는
형식으로 변경한다.
kubectl get secret regcred --output="jsonpath={.data.\.dockerconfigjson}" | base64 --decode
결과는 다음과 같다.
{"auths":{"your.private.registry.example.com":{"username":"janedoe","password":"xxxxxxxxxxx","email":"jdoe@example.com","auth":"c3R...zE2"}}}
auth
필드의 값을 확인하기 위하여, base64로 인코딩된 데이터를 읽을 수 있는 형식으로 변경한다.
echo "c3R...zE2" | base64 --decode
결과로, 사용자 이름과 비밀번호가 :
로 연결되어 아래와 같이 표현된다.
janedoe:xxxxxxxxxxx
참고로 시크릿 데이터에는 사용자의 로컬에 있는 ~/.docker/config.json
파일과 유사한 인증 토큰이 포함되어 있다.
이를 통해 regcred
라는 시크릿으로 클러스터 내에서 도커 자격 증명을 생성했다.
다음은 regcred
에 있는 도커 자격 증명에 접근해야 하는 예제 파드의 매니페스트이다.
apiVersion: v1
kind: Pod
metadata:
name: private-reg
spec:
containers:
- name: private-reg-container
image: <your-private-image>
imagePullSecrets:
- name: regcred
위 파일을 컴퓨터에 다운로드한다.
curl -L -o my-private-reg-pod.yaml https://k8s.io/examples/pods/private-reg-pod.yaml
my-private-reg-pod.yaml
파일 안에서, <your-private-image>
값을 다음과 같은 프라이빗 저장소 안의 이미지 경로로 변경한다.
your.private.registry.example.com/janedoe/jdoe-private:v1
프라이빗 저장소에서 이미지를 받아오기 위하여, 쿠버네티스에서 자격 증명이 필요하다.
구성 파일의 imagePullSecrets
필드를 통해 쿠버네티스가
regcred
라는 시크릿으로부터 자격 증명을 가져올 수 있다.
시크릿을 사용해서 파드를 생성하고, 파드가 실행되는지 확인하자.
kubectl apply -f my-private-reg-pod.yaml
kubectl get pod private-reg
imagePullSecrets
필드에 대해 읽어보기이 문서는 쿠버네티스 클러스터의 특정 노드에 노드 어피니티를 사용해 쿠버네티스 파드를 할당하는 방법을 설명한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.10. 버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
클러스터의 노드를 레이블과 함께 나열하자.
kubectl get nodes --show-labels
결과는 아래와 같다.
NAME STATUS ROLES AGE VERSION LABELS
worker0 Ready <none> 1d v1.13.0 ...,kubernetes.io/hostname=worker0
worker1 Ready <none> 1d v1.13.0 ...,kubernetes.io/hostname=worker1
worker2 Ready <none> 1d v1.13.0 ...,kubernetes.io/hostname=worker2
노드 한 개를 선택하고, 레이블을 추가하자.
kubectl label nodes <your-node-name> disktype=ssd
<your-node-name>
는 선택한 노드의 이름이다.
선택한 노드가 disktype=ssd
레이블을 갖고 있는지 확인하자.
kubectl get nodes --show-labels
결과는 아래와 같다.
NAME STATUS ROLES AGE VERSION LABELS
worker0 Ready <none> 1d v1.13.0 ...,disktype=ssd,kubernetes.io/hostname=worker0
worker1 Ready <none> 1d v1.13.0 ...,kubernetes.io/hostname=worker1
worker2 Ready <none> 1d v1.13.0 ...,kubernetes.io/hostname=worker2
위의 결과에서, worker0
노드에 disktype=ssd
레이블이 있는 것을
확인할 수 있다.
이 매니페스트는 disktype: ssd
라는 requiredDuringSchedulingIgnoredDuringExecution
노드 어피니티를 가진 파드를 설명한다.
파드가 disktype=ssd
레이블이 있는 노드에만 스케줄될 것이라는 것을 의미한다.
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
매니페스트를 적용하여 선택한 노드에 스케줄된 파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/pod-nginx-required-affinity.yaml
파드가 선택한 노드에서 실행 중인지 확인하자.
kubectl get pods --output=wide
결과는 아래와 같다.
NAME READY STATUS RESTARTS AGE IP NODE
nginx 1/1 Running 0 13s 10.200.0.4 worker0
이 매니페스트는 disktype: ssd
라는 preferredDuringSchedulingIgnoredDuringExecution
노드 어피니티를 가진 파드를 설명한다.
파드가 disktype=ssd
레이블이 있는 노드를 선호한다는 것을 의미한다.
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: disktype
operator: In
values:
- ssd
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
매니페스트를 적용하여 선택한 노드에 스케줄된 파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/pod-nginx-preferred-affinity.yaml
파드가 선택한 노드에서 실행 중인지 확인하자.
kubectl get pods --output=wide
결과는 아래와 같다.
NAME READY STATUS RESTARTS AGE IP NODE
nginx 1/1 Running 0 13s 10.200.0.4 worker0
노드 어피니티에 대해 더 알아보기.
이 문서는 쿠버네티스 클러스터의 특정 노드에 쿠버네티스 파드를 할당하는 방법을 설명한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
클러스터의 노드를 레이블과 함께 나열하자.
kubectl get nodes --show-labels
결과는 아래와 같다.
NAME STATUS ROLES AGE VERSION LABELS
worker0 Ready <none> 1d v1.13.0 ...,kubernetes.io/hostname=worker0
worker1 Ready <none> 1d v1.13.0 ...,kubernetes.io/hostname=worker1
worker2 Ready <none> 1d v1.13.0 ...,kubernetes.io/hostname=worker2
노드 한 개를 선택하고, 레이블을 추가하자.
kubectl label nodes <your-node-name> disktype=ssd
<your-node-name>
는 선택한 노드의 이름이다.
선택한 노드가 disktype=ssd
레이블을 갖고 있는지 확인하자.
kubectl get nodes --show-labels
결과는 아래와 같다.
NAME STATUS ROLES AGE VERSION LABELS
worker0 Ready <none> 1d v1.13.0 ...,disktype=ssd,kubernetes.io/hostname=worker0
worker1 Ready <none> 1d v1.13.0 ...,kubernetes.io/hostname=worker1
worker2 Ready <none> 1d v1.13.0 ...,kubernetes.io/hostname=worker2
위의 결과에서, worker0
노드에 disktype=ssd
레이블이 있는 것을
확인할 수 있다.
이 파드 구성 파일은 disktype: ssd
라는 선택하는 노드 셀렉터를 가진 파드를
설명한다.
즉, disktype=ssd
레이블이 있는 노드에 파드가 스케줄될 것이라는
것을 의미한다.
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd
구성 파일을 사용해서 선택한 노드로 스케줄되도록 파드를 생성하자.
kubectl apply -f https://k8s.io/examples/pods/pod-nginx.yaml
파드가 선택한 노드에서 실행 중인지 확인하자.
kubectl get pods --output=wide
결과는 아래와 같다.
NAME READY STATUS RESTARTS AGE IP NODE
nginx 1/1 Running 0 13s 10.200.0.4 worker0
nodeName
설정을 통해 특정 노드로 파드를 배포할 수 있다.
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
nodeName: foo-node # 특정 노드에 파드 스케줄
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
설정 파일을 사용해 foo-node
노드에 파드를 스케줄되도록 만들어 보자.
이 페이지는 애플리케이션 실행 전에 파드를 초기화하기 위해 어떻게 초기화 컨테이너를 구성해야 하는지 보여준다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
이 연습에서 하나의 애플리케이션 컨테이너와 하나의 초기화 컨테이너를 갖는 파드를 생성한다. 초기화 컨테이너는 애플리케이션 시작 전에 실행을 종료한다.
아래는 해당 파드의 구성 파일이다.
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
# 이 컨테이너들은 파드 초기화 중에 실행된다.
initContainers:
- name: install
image: busybox:1.28
command:
- wget
- "-O"
- "/work-dir/index.html"
- http://info.cern.ch
volumeMounts:
- name: workdir
mountPath: "/work-dir"
dnsPolicy: Default
volumes:
- name: workdir
emptyDir: {}
이 구성 파일에서, 파드가 가진 볼륨을 초기화 컨테이너와 애플리케이션 컨테이너가 공유하는 것을 볼 수 있다.
초기화 컨테이너는 공유된 볼륨을
/work-dir
에 마운트하고, 애플리케이션 컨테이너는 공유된 볼륨을
/usr/share/nginx/html
에 마운트한다. 초기화 컨테이너는 다음 명령을 실행 후
종료한다.
wget -O /work-dir/index.html http://info.cern.ch
초기화 컨테이너는 nginx 서버의 루트 디렉터리 내 index.html
파일을
저장한다.
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/init-containers.yaml
nginx 컨테이너가 실행 중인지 확인한다.
kubectl get pod init-demo
출력 결과는 nginx 컨테이너가 실행 중임을 보여준다.
NAME READY STATUS RESTARTS AGE
init-demo 1/1 Running 0 1m
init-demo 파드 내 실행 중인 nginx 컨테이너의 셸을 실행한다.
kubectl exec -it init-demo -- /bin/bash
셸에서 GET 요청을 nginx 서버로 전송한다.
root@nginx:~# apt-get update
root@nginx:~# apt-get install curl
root@nginx:~# curl localhost
출력 결과는 nginx가 초기화 컨테이너에 의해 저장된 웹 페이지를 제공하고 있음을 보여준다.
<html><head></head><body><header>
<title>http://info.cern.ch</title>
</header>
<h1>http://info.cern.ch - home of the first website</h1>
...
<li><a href="http://info.cern.ch/hypertext/WWW/TheProject.html">Browse the first website</a></li>
...
Kubernetes v1.25 [alpha]
이 페이지는 스테이트리스(stateless) 파드에 유저 네임스페이스를 구성하는 방법을 다룬다. 이를 통해 컨테이너 내부에서 실행 중인 유저를 호스트의 유저로부터 분리할 수 있다.
컨테이너에서 루트로 실행되는 프로세스는 호스트에서 다른(루트가 아닌) 유저로 실행할 수 있다. 즉, 프로세스는 유저 네임스페이스 내부의 작업에 대한 모든 권한을 갖지만 네임스페이스 외부의 작업에 대해서는 권한이 없다.
이 기능을 사용하여 손상된 컨테이너가 호스트 또는 동일한 노드의 다른 파드에 미칠 피해를 줄일 수 있다. 유저 네임스페이스를 이용하면, HIGH 또는 CRITICAL 로 분류되는 여러 보안 취약점을 보완할 수 있다. 유저 네임스페이스는 향후 발생할 수 있는 여러 취약점도 완화시킬 것으로 예상된다.
유저 네임스페이스를 사용하지 않고 루트로 실행하는 컨테이너는 컨테이너 브레이크아웃(breakout)이 발생하면 노드의 루트 권한을 갖는다. 그리고 컨테이너에 어떤 기능이 부여되어 있다면 해당 기능은 호스트에서도 유효하다. 유저 네임스페이스를 이용한다면 전혀 해당되지 않는 내용이다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.25. 버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
추가적으로, 쿠버네티스 스테이트리스(stateless) 파드에서 이 기능을 사용하려면 컨테이너 런타임에서 지원이 필요하다.
컨테이너 런타임이 유저 네임스페이스를 지원하지 않으면,
새 pod.spec
필드는 별다른 경고 없이 무시되고 파드는 유저 네임스페이스 없이 생성된다 는
사실을 명심한다.
스테이트리스 파드의 유저 네임스페이스는 .spec
의 hostUsers
필드를
false
로 설정하여 사용할 수 있다. 다음은 예시이다.
apiVersion: v1
kind: Pod
metadata:
name: userns
spec:
hostUsers: false
containers:
- name: shell
command: ["sleep", "infinity"]
image: debian
클러스터에 파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/user-namespaces-stateless.yaml
컨테이너에 연결하고 readlink /proc/self/ns/user
를 실행한다.
kubectl attach -it userns bash
그리고 명령을 실행한다. 결과는 다음과 유사하다.
readlink /proc/self/ns/user
user:[4026531837]
cat /proc/self/uid_map
0 0 4294967295
그런 다음 호스트에서 셸을 열고 동일한 명령을 실행한다.
결과는 분명 다를 것이다. 이는 호스트와 파드가 다른 유저 네임스페이스를 사용하고 있음을 의미한다. 유저 네임스페이스를 따로 만들지 않으면 호스트와 파드는 동일한 유저 네임스페이스를 사용한다.
유저 네임스페이스 내에서 kubelet을 실행하고 있다면, 파드에서 실행한 명령의 결과와 호스트에서 실행한 결과를 비교한다.
readlink /proc/$pid/ns/user
user:[4026534732]
$pid
은 kubelet의 PID로 대체한다.
스태틱 파드 는 API 서버 없이 특정 노드에 있는 kubelet 데몬에 의해 직접 관리된다. 컨트롤 플레인에 의해 관리되는 파드(예를 들어 디플로이먼트(Deployment))와는 달리, kubelet 이 각각의 스태틱 파드를 감시한다. (만약 실패할 경우 다시 구동한다.)
스태틱 파드는 항상 특정 노드에 있는 하나의 Kubelet에 매여 있다.
Kubelet 은 각각의 스태틱 파드에 대하여 쿠버네티스 API 서버에서 미러 파드(mirror pod)를 생성하려고 자동으로 시도한다. 즉, 노드에서 구동되는 파드는 API 서버에 의해서 볼 수 있지만, API 서버에서 제어될 수는 없다. 파드 이름에는 노드 호스트 이름 앞에 하이픈을 붙여 접미사로 추가된다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
이 페이지는 파드를 실행하기 위해 CRI-O를 사용하며, 노드에서 Fedora 운영 체제를 구동하고 있다고 가정한다. 다른 배포판이나 쿠버네티스 설치 지침과는 다소 상이할 수 있다.
파일 시스템이 호스팅하는 구성 파일이나 웹이 호스팅하는 구성 파일을 사용하여 스태틱 파드를 구성할 수 있다.
매니페스트는 특정 디렉터리에 있는 JSON 이나 YAML 형식의 표준 파드 정의이다.
kubelet 구성 파일의 staticPodPath: <the directory>
필드를 사용하자.
명시한 디렉터리를 정기적으로 스캔하여, 디렉터리 안의 YAML/JSON 파일이 생성되거나 삭제되었을 때 스태틱 파드를 생성하거나 삭제한다.
Kubelet 이 특정 디렉터리를 스캔할 때 점(.)으로 시작하는 단어를 무시한다는 점을 유의하자.
예를 들어, 다음은 스태틱 파드로 간단한 웹 서버를 구동하는 방법을 보여준다.
스태틱 파드를 실행할 노드를 선택한다. 이 예제에서는 my-model
이다.
ssh my-node1
/etc/kubernetes/manifests
와 같은 디렉터리를 선택하고 웹 서버 파드의 정의를 해당 위치에, 예를 들어 /etc/kubernetes/manifests/static-web.yaml
에 배치한다.
# kubelet 이 동작하고 있는 노드에서 이 명령을 수행한다.
mkdir -p /etc/kubernetes/manifests/
cat <<EOF >/etc/kubernetes/manifests/static-web.yaml
apiVersion: v1
kind: Pod
metadata:
name: static-web
labels:
role: myrole
spec:
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
protocol: TCP
EOF
노드에서 kubelet 실행 시에 --pod-manifest-path=/etc/kubernetes/manifests/
와 같이 인자를 제공하여 해당 디렉터리를 사용하도록 구성한다. Fedora 의 경우 이 줄을 포함하기 위하여 /etc/kubernetes/kubelet
파일을 다음과 같이 수정한다.
KUBELET_ARGS="--cluster-dns=10.254.0.10 --cluster-domain=kube.local --pod-manifest-path=/etc/kubernetes/manifests/"
혹은 kubelet 구성 파일에
staticPodPath: <the directory>
필드를 추가한다.
kubelet을 재시작한다. Fedora의 경우 아래와 같이 수행한다.
# kubelet 이 동작하고 있는 노드에서 이 명령을 수행한다.
systemctl restart kubelet
Kubelet은 --manifest-url=<URL>
의 인수로 지정된 파일을 주기적으로 다운로드하여
해당 파일을 파드의 정의가 포함된 JSON/YAML 파일로 해석한다.
파일시스템이 호스팅 하는 매니페스트 의 작동 방식과
유사하게 kubelet은 스케줄에 맞춰 매니페스트 파일을 다시 가져온다. 스태틱 파드의 목록에
변경된 부분이 있을 경우, kubelet 은 이를 적용한다.
이 방법을 사용하기 위하여 다음을 수행한다.
kubelet 에게 파일의 URL을 전달하기 위하여 YAML 파일을 생성하고 이를 웹 서버에 저장한다.
apiVersion: v1
kind: Pod
metadata:
name: static-web
labels:
role: myrole
spec:
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
protocol: TCP
선택한 노드에서 --manifest-url=<manifest-url>
을 실행하여 웹 메니페스트를 사용하도록 kubelet을 구성한다. Fedora 의 경우 이 줄을 포함하기 위하여 /etc/kubernetes/kubelet
파일을 수정한다.
KUBELET_ARGS="--cluster-dns=10.254.0.10 --cluster-domain=kube.local --manifest-url=<manifest-url>"
Kubelet을 재시작한다. Fedora의 경우 아래와 같이 수행한다.
# kubelet 이 동작하고 있는 노드에서 이 명령을 수행한다.
systemctl restart kubelet
Kubelet 을 시작하면, 정의된 모든 스태틱 파드가 자동으로 시작된다. 스태틱 파드를 정의하고, kubelet을 재시작했으므로, 새로운 스태틱 파드가 이미 실행 중이어야 한다.
(노드에서) 구동되고 있는 (스태틱 파드를 포함한) 컨테이너들을 볼 수 있다.
# kubelet 이 동작하고 있는 노드에서 이 명령을 수행한다.
crictl ps
결과는 다음과 유사하다.
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID
129fd7d382018 docker.io/library/nginx@sha256:... 11 minutes ago Running web 0 34533c6729106
crictl
은 이미지 URI와 SHA-256 체크섬을 출력한다. NAME
은 다음과 같을 것이다.
docker.io/library/nginx@sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
API 서버에서 미러 파드를 볼 수 있다.
kubectl get pods
NAME READY STATUS RESTARTS AGE
static-web 1/1 Running 0 2m
스태틱 파드에 있는 레이블 은 미러 파드로 전파된다. 셀렉터 등을 통하여 이러한 레이블을 사용할 수 있다.
만약 API 서버로부터 미러 파드를 지우기 위하여 kubectl
을 사용하려 해도,
kubelet 은 스태틱 파드를 지우지 않는다.
kubectl delete pod static-web
pod "static-web" deleted
파드가 여전히 구동 중인 것을 볼 수 있다.
kubectl get pods
NAME READY STATUS RESTARTS AGE
static-web 1/1 Running 0 4s
kubelet 이 구동 중인 노드로 돌아가서 컨테이너를 수동으로 중지할 수 있다. 일정 시간이 지나면, kubelet이 파드를 자동으로 인식하고 다시 시작하는 것을 볼 수 있다.
# kubelet 이 동작하고 있는 노드에서 이 명령을 수행한다.
crictl stop 129fd7d382018 # 예제를 수행하는 사용자의 컨테이너 ID로 변경한다.
sleep 20
crictl ps
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID
89db4553e1eeb docker.io/library/nginx@sha256:... 19 seconds ago Running web 1 34533c6729106
실행 중인 kubelet 은 주기적으로, 설정된 디렉터리(예제에서는 /etc/kubernetes/manifests
)에서 변경 사항을 스캔하고, 이 디렉터리에 새로운 파일이 생성되거나 삭제될 경우, 파드를 생성/삭제 한다.
# 예제를 수행하는 사용자가 파일시스템이 호스팅하는 스태틱 파드 설정을 사용한다고 가정한다.
# kubelet 이 동작하고 있는 노드에서 이 명령을 수행한다.
#
mv /etc/kubernetes/manifests/static-web.yaml /tmp
sleep 20
crictl ps
# 구동 중인 nginx 컨테이너가 없는 것을 확인한다.
mv /tmp/static-web.yaml /etc/kubernetes/manifests/
sleep 20
crictl ps
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID
f427638871c35 docker.io/library/nginx@sha256:... 19 seconds ago Running web 1 34533c6729106
Kompose는 무엇일까? Kompose는 컴포즈(즉, Docker Compose)를 컨테이너 오케스트레이션(쿠버네티스나 오픈시프트)으로 변환하는 도구이다.
더 자세한 내용은 Kompose 웹사이트 http://kompose.io에서 찾아볼 수 있다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
Kompose를 설치하기 위한 여러가지 방법들이 있다. 우리는 깃허브 최신 릴리스에서 바이너리를 다운 받는 방법을 사용할 것이다.
Kompose는 3주 주기로 깃허브에 릴리스된다. 현재 릴리스에 관한 모든 정보는 깃허브 릴리스 페이지에서 확인할 수 있다.
# Linux
curl -L https://github.com/kubernetes/kompose/releases/download/v1.26.0/kompose-linux-amd64 -o kompose
# macOS
curl -L https://github.com/kubernetes/kompose/releases/download/v1.26.0/kompose-darwin-amd64 -o kompose
# Windows
curl -L https://github.com/kubernetes/kompose/releases/download/v1.26.0/kompose-windows-amd64.exe -o kompose.exe
chmod +x kompose
sudo mv ./kompose /usr/local/bin/kompose
또 다른 방법으로, tarball를 다운로드 받을 수 있다.
go get
을 통해 설치를 진행하면 최신 개발 변경점을 담고 있는 마스터 브랜치를 pull 한다.
go get -u github.com/kubernetes/kompose
Kompose는 EPEL CentOS 저장소이다.
만약 EPEL 저장소를 설치하고 활성화하지 않았다면, sudo yum install epel-release
으로 이를 수행할 수 있다.
시스템에 EPEL이 활성화되어 있다면, 다른 패키지처럼 Kompose를 설치할 수 있다.
sudo yum -y install kompose
Kompose는 페도라 24, 25, 그리고 26 저장소에 있다. 다른 패키지들처럼 설치할 수 있다.
sudo dnf -y install kompose
macOS에서는 Homebrew를 통해 최신 릴리스를 설치할 수 있다.
brew install kompose
몇 단계를 수행하면, 도커 컴포즈를 쿠버네티스로 변환할 수 있다.
docker-compose.yml
파일만 있으면 된다.
docker-compose.yml
파일이 존재하는 디렉토리로 이동한다. 만약 없다면, 아래 예제로 테스트한다.
version: "2"
services:
redis-master:
image: registry.k8s.io/redis:e2e
ports:
- "6379"
redis-slave:
image: gcr.io/google_samples/gb-redisslave:v3
ports:
- "6379"
environment:
- GET_HOSTS_FROM=dns
frontend:
image: gcr.io/google-samples/gb-frontend:v4
ports:
- "80:80"
environment:
- GET_HOSTS_FROM=dns
labels:
kompose.service.type: LoadBalancer
docker-compose.yml
파일을 kubectl
를 통해 활용할 수 있는 파일들로 변환하기 위해서는,
kompose convert
를 실행한 후, kubectl apply -f <output file>
를 실행한다.
kompose convert
결과는 다음과 같다.
INFO Kubernetes file "frontend-service.yaml" created
INFO Kubernetes file "frontend-service.yaml" created
INFO Kubernetes file "frontend-service.yaml" created
INFO Kubernetes file "redis-master-service.yaml" created
INFO Kubernetes file "redis-master-service.yaml" created
INFO Kubernetes file "redis-master-service.yaml" created
INFO Kubernetes file "redis-slave-service.yaml" created
INFO Kubernetes file "redis-slave-service.yaml" created
INFO Kubernetes file "redis-slave-service.yaml" created
INFO Kubernetes file "frontend-deployment.yaml" created
INFO Kubernetes file "frontend-deployment.yaml" created
INFO Kubernetes file "frontend-deployment.yaml" created
INFO Kubernetes file "redis-master-deployment.yaml" created
INFO Kubernetes file "redis-master-deployment.yaml" created
INFO Kubernetes file "redis-master-deployment.yaml" created
INFO Kubernetes file "redis-slave-deployment.yaml" created
INFO Kubernetes file "redis-slave-deployment.yaml" created
INFO Kubernetes file "redis-slave-deployment.yaml" created
kubectl apply -f frontend-service.yaml,redis-master-service.yaml,redis-slave-service.yaml,frontend-deployment.yaml,redis-master-deployment.yaml,redis-slave-deployment.yaml
결과는 다음과 같다.
service/frontend created
service/redis-master created
service/redis-slave created
deployment.apps/frontend created
deployment.apps/redis-master created
deployment.apps/redis-slave created
디플로이먼트들은 쿠버네티스에서 실행된다.
애플리케이션에 접근하기.
minikube
를 개발 환경으로 사용하고 있다면
minikube service frontend
이 외에는, 서비스가 사용중인 IP를 확인해보자!
kubectl describe svc frontend
Name: frontend
Namespace: default
Labels: service=frontend
Selector: service=frontend
Type: LoadBalancer
IP: 10.0.0.183
LoadBalancer Ingress: 192.0.2.89
Port: 80 80/TCP
NodePort: 80 31144/TCP
Endpoints: 172.17.0.4:80
Session Affinity: None
No events.
클라우드 환경을 사용하고 있다면, IP는 LoadBalancer Ingress
옆에 나열되어 있을 것이다.
curl http://192.0.2.89
Kompose는 오픈시프트와 쿠버네티스 두 제공자를 지원한다.
--provider
글로벌 옵션을 통해 대상 제공자를 선택할 수 있다. 만약 제공자가 명시되지 않았다면, 쿠버네티스가 기본값으로 설정된다.
kompose convert
Kompose는 도커 컴포즈 V1, V2, 그리고 V3 파일에 대한 쿠버네티스와 오픈시프트 오브젝트로의 변환을 지원한다.
kompose convert
예제kompose --file docker-voting.yml convert
WARN Unsupported key networks - ignoring
WARN Unsupported key build - ignoring
INFO Kubernetes file "worker-svc.yaml" created
INFO Kubernetes file "db-svc.yaml" created
INFO Kubernetes file "redis-svc.yaml" created
INFO Kubernetes file "result-svc.yaml" created
INFO Kubernetes file "vote-svc.yaml" created
INFO Kubernetes file "redis-deployment.yaml" created
INFO Kubernetes file "result-deployment.yaml" created
INFO Kubernetes file "vote-deployment.yaml" created
INFO Kubernetes file "worker-deployment.yaml" created
INFO Kubernetes file "db-deployment.yaml" created
ls
db-deployment.yaml docker-compose.yml docker-gitlab.yml redis-deployment.yaml result-deployment.yaml vote-deployment.yaml worker-deployment.yaml
db-svc.yaml docker-voting.yml redis-svc.yaml result-svc.yaml vote-svc.yaml worker-svc.yaml
동시에 여러 도커 컴포즈 파일들을 명시할 수도 있다
kompose -f docker-compose.yml -f docker-guestbook.yml convert
INFO Kubernetes file "frontend-service.yaml" created
INFO Kubernetes file "mlbparks-service.yaml" created
INFO Kubernetes file "mongodb-service.yaml" created
INFO Kubernetes file "redis-master-service.yaml" created
INFO Kubernetes file "redis-slave-service.yaml" created
INFO Kubernetes file "frontend-deployment.yaml" created
INFO Kubernetes file "mlbparks-deployment.yaml" created
INFO Kubernetes file "mongodb-deployment.yaml" created
INFO Kubernetes file "mongodb-claim0-persistentvolumeclaim.yaml" created
INFO Kubernetes file "redis-master-deployment.yaml" created
INFO Kubernetes file "redis-slave-deployment.yaml" created
ls
mlbparks-deployment.yaml mongodb-service.yaml redis-slave-service.jsonmlbparks-service.yaml
frontend-deployment.yaml mongodb-claim0-persistentvolumeclaim.yaml redis-master-service.yaml
frontend-service.yaml mongodb-deployment.yaml redis-slave-deployment.yaml
redis-master-deployment.yaml
만약 여러 도커 컴포즈 파일들이 명시되면 설정들은 병합된다. 공통적인 설정들은 그 다음 파일의 내용으로 덮어씌워진다.
kompose convert
예제kompose --provider openshift --file docker-voting.yml convert
WARN [worker] Service cannot be created because of missing port.
INFO OpenShift file "vote-service.yaml" created
INFO OpenShift file "db-service.yaml" created
INFO OpenShift file "redis-service.yaml" created
INFO OpenShift file "result-service.yaml" created
INFO OpenShift file "vote-deploymentconfig.yaml" created
INFO OpenShift file "vote-imagestream.yaml" created
INFO OpenShift file "worker-deploymentconfig.yaml" created
INFO OpenShift file "worker-imagestream.yaml" created
INFO OpenShift file "db-deploymentconfig.yaml" created
INFO OpenShift file "db-imagestream.yaml" created
INFO OpenShift file "redis-deploymentconfig.yaml" created
INFO OpenShift file "redis-imagestream.yaml" created
INFO OpenShift file "result-deploymentconfig.yaml" created
INFO OpenShift file "result-imagestream.yaml" created
서비스 내 빌드 명령에 대한 빌드컨피그(buildconfig) 생성도 지원한다. 기본적으로, 현재 깃 브랜치에 대한 리모트 저장소를 소스 저장소로 사용한다. 그리고 현재 브랜치를 빌드를 위한 소스 브랜치로 사용한다. --build-repo
와 --build-branch
옵션으로 다른 소스 저장소와 브랜치를 각각 지정할 수 있다.
kompose --provider openshift --file buildconfig/docker-compose.yml convert
WARN [foo] Service cannot be created because of missing port.
INFO OpenShift Buildconfig using git@github.com:rtnpro/kompose.git::master as source.
INFO OpenShift file "foo-deploymentconfig.yaml" created
INFO OpenShift file "foo-imagestream.yaml" created
INFO OpenShift file "foo-buildconfig.yaml" created
oc create -f
로 오픈시프트 아티팩트들을 수동으로 푸쉬한다면, 이미지스트림 아티팩트를 빌드컨피그 아티팩트 이전에 푸쉬해야 한다. 해당 오픈시프트 이슈에 대한 해결방안은 https://github.com/openshift/origin/issues/4518를 참고한다.kompose
의 기본 변환은 쿠버네티스 디플로이먼트와 서비스를 yaml 형식으로 생성하는 것이다. 또 다른 방법으로는 -j
옵션으로 json을 생성할 수도 있다. 또한, 레플리케이션 컨트롤러 오브젝트와, 데몬셋, Helm 차트를 생성할 수도 있다.
kompose convert -j
INFO Kubernetes file "redis-svc.json" created
INFO Kubernetes file "web-svc.json" created
INFO Kubernetes file "redis-deployment.json" created
INFO Kubernetes file "web-deployment.json" created
*-deployment.json
파일은 디플로이먼트 오브젝트들을 담고 있다.
kompose convert --replication-controller
INFO Kubernetes file "redis-svc.yaml" created
INFO Kubernetes file "web-svc.yaml" created
INFO Kubernetes file "redis-replicationcontroller.yaml" created
INFO Kubernetes file "web-replicationcontroller.yaml" created
*-replicationcontroller.yaml
파일들은 레플리케이션 컨트롤러 오브젝트들을 담고 있다. 만약 레플리카를 명시하고 싶다면, --replicas
플래그를 사용한다. kompose convert --replication-controller --replicas 3
.
kompose convert --daemon-set
INFO Kubernetes file "redis-svc.yaml" created
INFO Kubernetes file "web-svc.yaml" created
INFO Kubernetes file "redis-daemonset.yaml" created
INFO Kubernetes file "web-daemonset.yaml" created
*-daemonset.yaml
파일들은 데몬셋 오브젝트를 담고 있다.
만약 헬름을 통해 차트를 생성하고 싶다면, 아래 명령을 실행한다.
kompose convert -c
INFO Kubernetes file "web-svc.yaml" created
INFO Kubernetes file "redis-svc.yaml" created
INFO Kubernetes file "web-deployment.yaml" created
INFO Kubernetes file "redis-deployment.yaml" created
chart created in "./docker-compose/"
tree docker-compose/
docker-compose
├── Chart.yaml
├── README.md
└── templates
├── redis-deployment.yaml
├── redis-svc.yaml
├── web-deployment.yaml
└── web-svc.yaml
차트 구조는 헬름 차트를 만들기 위한 골격을 제공한다.
kompose
는 서비스의 행동을 변환 시에 명시적으로 정의하기 위해 Kompose에 특화된 레이블들을 docker-compose.yml
파일에서 지원한다.
kompose.service.type
는 서비스의 종류를 정의한다.
예를 들어
version: "2"
services:
nginx:
image: nginx
dockerfile: foobar
build: ./foobar
cap_add:
- ALL
container_name: foobar
labels:
kompose.service.type: nodeport
kompose.service.expose
는 서비스가 클러스터 외부에서 접근 가능한지를 정의한다. 값이 "true"로 설정되어 있다면, 제공자는 자동으로 엔드포인트를 설정한다. 이외의 값의 경우에는, 호스트네임으로 값이 설정된다. 서비스에 여러 포드들이 정의되어 있다면, 첫번째 포트가 선택되어 노출된다.
예를 들어:
version: "2"
services:
web:
image: tuna/docker-counter23
ports:
- "5000:5000"
links:
- redis
labels:
kompose.service.expose: "counter.example.com"
redis:
image: redis:3.0
ports:
- "6379"
현재 지원하는 옵션들은 다음과 같다.
Key | Value |
---|---|
kompose.service.type | nodeport / clusterip / loadbalancer |
kompose.service.expose | true / hostname |
kompose.service.type
레이블은 ports
만을 정의해야 한다. 이 외의 경우에는 kompose
가 실패한다.만약 컨트롤러 없이 일반 파드들을 생성하고 싶다면, 도커 컴포즈에 restart
를 명시하여 이를 정의한다. restart
값을 설정하였을 때 어떤 일이 일어나는지는 아래 표를 확인한다.
docker-compose restart |
object created | Pod restartPolicy |
---|---|---|
"" |
controller object | Always |
always |
controller object | Always |
on-failure |
Pod | OnFailure |
no |
Pod | Never |
deployment
이나 replicationcontroller
가 될 수 있다.예를 들어, pival
서비스는 아래 파드가 될 것이다. 이 컨테이너의 계산된 값은 pi
이다.
version: '2'
services:
pival:
image: perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restart: "on-failure"
만약 도커 컴포즈 파일에 서비스를 위한 볼륨이 명시되어 있다면, 디플로이먼트(쿠버네티스)나 디플로이먼트컨피그(오픈시프트) 전략은 "롤링 업데이트"(기본값)이 아닌 "재생성"으로 변경된다. 이것은 서비스의 여러 인스턴스들이 동시에 같은 볼륨에 접근하는 것을 막기 위함이다.
만약 도커 컴포즈 파일의 서비스 이름에 _
이 포함되어 있다면 (예를 들어, web_service
와 같은), -
으로 대체되고 서비스의 이름 또한 마찬가지로 변경될 것이다 (예를 들어, web-service
로). 이는 쿠버네티스가 오브젝트 이름에 _
를 허용하지 않기 때문이다.
서비스 이름을 변경할 경우 docker-compose
파일의 일부가 작동하지 않을 수도 있다.
Kompose는 다음의 도커 컴포즈 버전을 지원한다. 1, 2, 그리고 3. 버전 2.1와 3.2는 실험적인 환경이기 때문에 제한적으로 지원한다.
호환하지 않는 도커 컴포즈 키 리스트를 포함한 3개의 버전들에 관한 모든 호환성 리스트는 변환 문서에서 확인 할 수 있다.
쿠버네티스 오브젝트는 여러 개의 오브젝트 구성 파일을
디렉터리에 저장하고 필요에 따라 kubectl apply
를
사용하여 재귀적으로 오브젝트를 생성하고 업데이트함으로써 생성, 업데이트 및 삭제할 수 있다.
이 방식은 변경사항을 되돌려 오브젝트 구성 파일에 병합하지 않고
활성 오브젝트에 가해진 기록을 유지한다. kubectl diff
는 또한
apply
가 어떠한 변경사항을 이루어질지에 대한 프리뷰를 제공한다.
kubectl
를 설치한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
kubectl
툴은 세 가지 방식의 오브젝트 관리를 지원한다.
오브젝트 관리 방식의 종류별 장단점에 대한 논의는 쿠버네티스 오브젝트 관리를 참고한다.
선언형 오브젝트 구성은 쿠버네티스 오브젝트 정의와 구성에 대한 확실한 이해가 필요하다. 아직 그렇지 못하다면, 먼저 다음 문서를 읽고 이해한다.
다음은 이 문서에서 사용되는 용어에 대한 정의이다.
kubectl apply
에 구성 파일을 전달하는지에 대해 보여준다. 구성 파일은 일반적으로 Git과 같은, 소스 컨트롤에 저장된다.kubectl apply
를 실행하여 변경사항을 기록한다.기존에 존재하는 것을 제외한, 지정한 디렉터리 내 구성 파일에 의해 정의된 모든 오브젝트를 생성하기 위해 kubectl apply
를
사용한다.
kubectl apply -f <디렉터리>/
이것은 각 오브젝트에 대해 kubectl.kubernetes.io/last-applied-configuration: '{...}'
어노테이션을 설정한다. 해당 어노테이션은 오브젝트를 생성하기 위해 사용했던
오브젝트 구성 파일의 내용을 포함한다.
-R
플래그를 추가한다.다음은 오브젝트 구성 파일에 대한 예시이다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
minReadySeconds: 5
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
생성될 오브젝트를 출력하려면 kubectl diff
를 실행한다.
kubectl diff -f https://k8s.io/examples/application/simple_deployment.yaml
diff
는 kube-apiserver
의 활성화가 필요한
서버사이드 dry-run을 사용한다.
diff
는 dry-run 모드에서 서버 측 적용 요청을 수행하므로,
PATCH
, CREATE
, 그리고 UPDATE
권한을 부여해야 한다.
자세한 것은
Dry-Run 인증을 본다.
kubectl apply
를 사용하여 오브젝트를 생성한다.
kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml
kubectl get
을 사용하여 활성 구성을 출력한다.
kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml
출력은 kubectl.kubernetes.io/last-applied-configuration
어노테이션이
활성 구성에 기록된 것을 보여주며, 그것은 구성 파일과 일치한다.
kind: Deployment
metadata:
annotations:
# ...
# This is the json representation of simple_deployment.yaml
# It was written by kubectl apply when the object was created
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apps/v1","kind":"Deployment",
"metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
"spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
"spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
"ports":[{"containerPort":80}]}]}}}}
# ...
spec:
# ...
minReadySeconds: 5
selector:
matchLabels:
# ...
app: nginx
template:
metadata:
# ...
labels:
app: nginx
spec:
containers:
- image: nginx:1.14.2
# ...
name: nginx
ports:
- containerPort: 80
# ...
# ...
# ...
# ...
또한 오브젝트가 기존에 존재하더라도 디렉터리 내 정의된 모든 오브젝트를 업데이트하기 위해 kubectl apply
를
사용할 수 있다. 이러한 접근방식은 다음을 수행할 수 있게 해준다.
kubectl diff -f <디렉터리>/
kubectl apply -f <디렉터리>/
-R
플래그를 추가한다.다음은 구성 파일의 예시이다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
minReadySeconds: 5
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
kubectl apply
를 사용하여 오브젝트를 생성한다.
kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml
kubectl get
을 사용하여 활성 구성을 출력한다.
kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml
출력은 kubectl.kubernetes.io/last-applied-configuration
어노테이션이
활성 구성에 기록된 것을 보여주며, 그것은 구성 파일과 일치한다.
kind: Deployment
metadata:
annotations:
# ...
# This is the json representation of simple_deployment.yaml
# It was written by kubectl apply when the object was created
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apps/v1","kind":"Deployment",
"metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
"spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
"spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
"ports":[{"containerPort":80}]}]}}}}
# ...
spec:
# ...
minReadySeconds: 5
selector:
matchLabels:
# ...
app: nginx
template:
metadata:
# ...
labels:
app: nginx
spec:
containers:
- image: nginx:1.14.2
# ...
name: nginx
ports:
- containerPort: 80
# ...
# ...
# ...
# ...
kubectl scale
을 사용하여 활성 구성 내 replicas
필드를 직접 업데이트한다.
이는 kubectl apply
를 사용하지 않는다.
kubectl scale deployment/nginx-deployment --replicas=2
kubectl get
을 사용하여 활성 구성을 출력한다.
kubectl get deployment nginx-deployment -o yaml
출력은 replicas
필드가 2로 설정된 것을 보여주며, last-applied-configuration
어노테이션은 replicas
필드를 포함하지 않는다.
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
# ...
# note that the annotation does not contain replicas
# because it was not updated through apply
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apps/v1","kind":"Deployment",
"metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
"spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
"spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
"ports":[{"containerPort":80}]}]}}}}
# ...
spec:
replicas: 2 # written by scale
# ...
minReadySeconds: 5
selector:
matchLabels:
# ...
app: nginx
template:
metadata:
# ...
labels:
app: nginx
spec:
containers:
- image: nginx:1.14.2
# ...
name: nginx
ports:
- containerPort: 80
# ...
nginx:1.14.2
에서 nginx:1.16.1
로 이미지를 변경하기 위해 simple_deployment.yaml
구성 파일을 업데이트 하고, minReadySeconds
필드를 삭제한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.16.1 # update the image
ports:
- containerPort: 80
구성 파일에 이루어진 변경사항을 적용한다.
kubectl diff -f https://k8s.io/examples/application/update_deployment.yaml
kubectl apply -f https://k8s.io/examples/application/update_deployment.yaml
kubectl get
을 사용하여 활성 구성을 출력한다.
kubectl get -f https://k8s.io/examples/application/update_deployment.yaml -o yaml
출력은 활성 구성에 다음의 변경사항을 보여준다.
replicas
필드는 kubectl scale
에 의해 설정된 값 2를 유지한다.image
필드는 nginx:1.14.2
에서 nginx:1.16.1
로 업데이트되었다.last-applied-configuration
어노테이션은 새로운 이미지로 업데이트되었다.minReadySeconds
필드는 지워졌다.last-applied-configuration
어노테이션은 더 이상 minReadySeconds
필드를 포함하지 않는다.apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
# ...
# The annotation contains the updated image to nginx 1.11.9,
# but does not contain the updated replicas to 2
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apps/v1","kind":"Deployment",
"metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
"spec":{"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
"spec":{"containers":[{"image":"nginx:1.16.1","name":"nginx",
"ports":[{"containerPort":80}]}]}}}}
# ...
spec:
replicas: 2 # Set by `kubectl scale`. Ignored by `kubectl apply`.
# minReadySeconds cleared by `kubectl apply`
# ...
selector:
matchLabels:
# ...
app: nginx
template:
metadata:
# ...
labels:
app: nginx
spec:
containers:
- image: nginx:1.16.1 # Set by `kubectl apply`
# ...
name: nginx
ports:
- containerPort: 80
# ...
# ...
# ...
# ...
create
와 replace
와 함께 kubectl apply
를
혼합하는 것은 지원하지 않는다. 이는 kubectl apply
가 업데이트 사항을 계산하는데 사용하는
kubectl.kubernetes.io/last-applied-configuration
을 create
와 replace
가
유지하지 하지 않기 때문이다.kubectl apply
에 의해 관리되는 오브젝트를 삭제하는데 2가지 접근 방법이 있다.
kubectl delete -f <파일명>
명령형 커맨드를 사용하여 오브젝트를 수동으로 삭제하는 것이 권장되는 방식인데, 무엇이 삭제되는지에 대해 더 명확하게 나타내므로 사용자가 의도하지 않게 무언가를 삭제할 가능성이 작아지기 때문이다.
kubectl delete -f <파일명>
kubectl apply -f <디렉터리/> --prune -l your=레이블
무엇을 하는지 파악하는 경우에만 이를 사용한다.
kubectl apply --prune
은 알파 상태이며, 후속 릴리스에서는
하위 호환되지 않는 변경 사항이 도입될 수 있다.kubectl delete
에 대한 대안으로, 디렉터리로부터 구성 파일이 삭제된 후에 삭제될 오브젝트를 식별하기 위해 kubectl apply
를 사용할 수 있다.
--prune
을 사용하여 적용하면 일련의 레이블의 집합과 일치하는
모든 오브젝트에 대해API 서버에 쿼리하고, 반환된 활성 오브젝트
구성을 오브젝트 구성 파일에 일치시키려고 시도한다.
오브젝트가 쿼리에 일치하고, 해당 디렉터리 내 구성 파일이 없고
last-applied-configuration
어노테이션이 있는 경우,
삭제된다.
kubectl apply -f <디렉터리/> --prune -l <레이블>
-l <레이블>
로 지정된 레이블 셀렉터에 의해 반환되고 하위 디렉터리에 나타나지 않는 경우,
오브젝트가 의도하지 않게 삭제될 수 있다.활성 오브젝트의 구성을 확인하기 위해 -o yaml
과 함께 kubectl get
을 사용할 수 있다.
kubectl get -f <파일명|url> -o yaml
kubectl apply
가 하나의 오브젝트에 대한 활성 구성을 업데이트할 때,
API 서버에 패치 요청을 보냄으로써 그것을 수행한다.
그 패치는 활성 오브젝트 구성의 특정 필드에 대한 범위의
업데이트로 한정한다. kubectl apply
커맨드는
구성 파일, 활성 구성, 그리고 활성 구성에 저장된
last-applied-configuration
어노테이션을 사용하여 이 패치 요청을 계산한다.
kubectl apply
명령은
kubectl.kubernetes.io/last-applied-configuration
어노테이션에 구성 파일의 내용을 기록한다.
이것은 구성 파일로부터 제거되었고 활성 구성으로부터 지워질 필요가 있는
필드를 확인하는 데 사용된다. 다음은 어떤 필드가 삭제 또는 설정돼야 하는지
계산하기 위해 사용되는 단계이다.
last-applied-configuration
내 존재하고 구성 파일로부터 유실된 필드이다.다음은 예시이다. 디플로이먼트 오브젝트에 대한 구성 파일이라고 가정한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.16.1 # update the image
ports:
- containerPort: 80
또한, 이것은 동일한 디플로이먼트 오브젝트에 대한 활성 구성이라고 가정한다.
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
# ...
# note that the annotation does not contain replicas
# because it was not updated through apply
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apps/v1","kind":"Deployment",
"metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
"spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
"spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
"ports":[{"containerPort":80}]}]}}}}
# ...
spec:
replicas: 2 # written by scale
# ...
minReadySeconds: 5
selector:
matchLabels:
# ...
app: nginx
template:
metadata:
# ...
labels:
app: nginx
spec:
containers:
- image: nginx:1.14.2
# ...
name: nginx
ports:
- containerPort: 80
# ...
다음은 kubectl apply
에 의해 수행될 병합 계산이다.
last-applied-configuration
으로부터 값을 읽어
구성 파일의 값과 비교하여 삭제할 필드를
계산한다.
last-applied-configuration
에 보이는 것과는 무관하게
로컬의 오브젝트 구성 파일 내 null이라고 명시적으로 설정된 필드를 지운다.
이 예시에서, minReadySeconds
은
last-applied-configuration
어노테이션 내 나타나지만, 구성 파일 내에는 보여지지 않는다.
조치: 활성 구성으로부터 minReadySeconds
을 지운다.image
값은 활성 구성 내 값과 불일치한다.
조치: 활성 구성 내 image
값을 설정한다.last-applied-configuration
어노테이션을 설정한다.다음은 병합의 결과인 활성 구성이다.
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
# ...
# The annotation contains the updated image to nginx 1.11.9,
# but does not contain the updated replicas to 2
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apps/v1","kind":"Deployment",
"metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
"spec":{"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
"spec":{"containers":[{"image":"nginx:1.16.1","name":"nginx",
"ports":[{"containerPort":80}]}]}}}}
# ...
spec:
selector:
matchLabels:
# ...
app: nginx
replicas: 2 # Set by `kubectl scale`. Ignored by `kubectl apply`.
# minReadySeconds cleared by `kubectl apply`
# ...
template:
metadata:
# ...
labels:
app: nginx
spec:
containers:
- image: nginx:1.16.1 # Set by `kubectl apply`
# ...
name: nginx
ports:
- containerPort: 80
# ...
# ...
# ...
# ...
구성 파일 내 특정 필드가 필드의 타입에 따라 어떻게 활성 구성과 함께 병합되는가. 여러 가지 필드 타입이 있다.
기본(primitives): 문자열, 숫자 또는 불리언 타입의 필드.
예를 들어, image
와 replicas
는 기본 필드다. 조치: 교체.
맵, 또한 오브젝트 라 칭함: 맵 타입 또는 서브필드를 포함하는 복합 타입의 필드. 예를 들어, 레이블
,
어노테이션
,스펙
및 메타데이터
는 모두 맵이다. 조치: 구성요소 또는 서브필드 병합.
리스트: 기본타입 또는 맵이 될 수 있는 아이템의 리스트를 포함하는 필드.
예를 들어, 컨테이너
, 포트
, 그리고 args
는 리스트다. 조치: 다양함.
kubectl apply
가 맵 또는 리스트 필드를 업데이트하는 경우,
일반적으로 전체 필드를 교체하는 대신, 개별 부 구성요소를 업데이트한다,
예를 들어, 디플로이먼트에 대한 spec
을 병합할 경우, 전체 spec
이
교체되지 않는다. 대신 replicas
와 같은 spec
의 서브필드가
비교되고 병합된다.
기본 필드는 교체되거나 지워진다.
-
는 값이 사용되지 않기 때문에 "해당 없음"으로 사용된다.Field in object configuration file | Field in live object configuration | Field in last-applied-configuration | Action |
---|---|---|---|
Yes | Yes | - | 구성 파일 값 활성으로 설정. |
Yes | No | - | 활성을 로컬 구성으로 설정. |
No | - | Yes | 활성 구성으로부터 지움. |
No | - | No | 아무것도 안함. 활성값 유지. |
맵을 요청하는 필드는 서브필드의 각각 또는 맵의 구성요소를 비교함으로써 병합된다.
-
는 값이 사용되지 않기 때문에 "해당 없음"으로 사용된다.Key in object configuration file | Key in live object configuration | Field in last-applied-configuration | Action |
---|---|---|---|
Yes | Yes | - | 서브필드 값 비교. |
Yes | No | - | 활성을 로컬 구성으로 설정. |
No | - | Yes | 활성 구성으로부터 삭제. |
No | - | No | 아무것도 안함. 활성값 유지. |
리스트에 대한 변경사항을 병합하는 것은 세 가지 전략 중 하나를 사용한다.
전략에 대한 선택은 필드별로 이루어진다.
기초 필드와 동일한 리스트로 취급한다. 전체 리스트를 교체 또는 삭제한다. 이것은 순서를 유지한다.
예시: 파드 내 컨테이너의 args
필드를 업데이트하기 위해 kubectl apply
를 사용한다.
이것은 활성 구성 내 args
의 값을 구성 파일 내 값으로 설정한다.
활성 구성에 추가했던 이전의 모든 args
구성요소들은 유실된다.
구성 파일 내 정의한 args
구성요소의 순서는
활성 구성 내 유지된다.
# last-applied-configuration value
args: ["a", "b"]
# configuration file value
args: ["a", "c"]
# live configuration
args: ["a", "b", "d"]
# result after merge
args: ["a", "c"]
설명: 병합은 새로운 리스트 값으로 구성 파일 값을 사용했다.
리스트를 맵으로 취급하고 각 구성요소의 특정 필드를 키로 취급한다. 개별 구성요소를 추가, 삭제, 또는 업데이트 한다. 이것은 순서를 보존하지 않는다.
이 병합 전략은 각 필드에 patchMergeKey
라 칭하는 특별한 태그를 사용한다.
patchMergeKey
는 쿠버네티스 소스 코드:
types.go
의 각 필드에 대해 정의한다. 맵 리스트를 병합할 때, 주어진 구성요소에 대한 patchMergeKey
로
지정한 필드는 해당 구성요소에 대한 맵키와 같이 사용된다.
예시: kubectl apply
를 사용하여 PodSpec에 대한 containers
필드를 업데이트한다.
이렇게 하면 각 구성요소가
name
별로 키로 되어 있는 맵인 것처럼 리스트를 병합한다.
# last-applied-configuration value
containers:
- name: nginx
image: nginx:1.10
- name: nginx-helper-a # key: nginx-helper-a; will be deleted in result
image: helper:1.3
- name: nginx-helper-b # key: nginx-helper-b; will be retained
image: helper:1.3
# configuration file value
containers:
- name: nginx
image: nginx:1.10
- name: nginx-helper-b
image: helper:1.3
- name: nginx-helper-c # key: nginx-helper-c; will be added in result
image: helper:1.3
# live configuration
containers:
- name: nginx
image: nginx:1.10
- name: nginx-helper-a
image: helper:1.3
- name: nginx-helper-b
image: helper:1.3
args: ["run"] # Field will be retained
- name: nginx-helper-d # key: nginx-helper-d; will be retained
image: helper:1.3
# result after merge
containers:
- name: nginx
image: nginx:1.10
# Element nginx-helper-a was deleted
- name: nginx-helper-b
image: helper:1.3
args: ["run"] # Field was retained
- name: nginx-helper-c # Element was added
image: helper:1.3
- name: nginx-helper-d # Element was ignored
image: helper:1.3
설명:
args
에kubectl apply
는args
가 없음) 활성 구성에patchMergeKey
필드 값(이름)이 둘 다 같았기 때문이다..쿠버네티스 1.5로부터 기초 구성요소 병합하기는 지원되지 않는다.
patchStrategy
태그에 의해 제어된다.
타입 필드에 대해 patchStrategy
가 지정되지 않으면,
리스트는 대체된다.오브젝트가 생성될 때 값이 지정되지 않는 경우, API 서버는 활성 구성 내 특정 필드를 기본값으로 설정한다.
다음은 디플로이먼트에 대한 구성 파일이다. 파일에는 strategy
가 지정되지 않았다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
minReadySeconds: 5
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
kubectl apply
를 사용하여 오브젝트를 생성한다.
kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml
kubectl get
을 사용하여 활성 구성을 출력한다.
kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml
출력은 API 서버가 활성 구성 내 여러 필드를 기본값으로 설정한 것을 보여준다. 이 필드들은 구성 파일에 지정되지 않았다.
apiVersion: apps/v1
kind: Deployment
# ...
spec:
selector:
matchLabels:
app: nginx
minReadySeconds: 5
replicas: 1 # defaulted by apiserver
strategy:
rollingUpdate: # defaulted by apiserver - derived from strategy.type
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate # defaulted by apiserver
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx:1.14.2
imagePullPolicy: IfNotPresent # defaulted by apiserver
name: nginx
ports:
- containerPort: 80
protocol: TCP # defaulted by apiserver
resources: {} # defaulted by apiserver
terminationMessagePath: /dev/termination-log # defaulted by apiserver
dnsPolicy: ClusterFirst # defaulted by apiserver
restartPolicy: Always # defaulted by apiserver
securityContext: {} # defaulted by apiserver
terminationGracePeriodSeconds: 30 # defaulted by apiserver
# ...
패치 요청에서, 패치 요청의 부분으로서 명시적으로 지워지지 않은 경우 기본 처리된 필드는 다시 기본으로 설정되지 않는다. 이것은 다른 필드에 대한 값에 따라 기본 처리된 필드에 대해 예상하지 못한 동작을 유발할 수 있다. 다른 필드가 나중에 변경되면, 그로부터 기본 처리된 것이 명시적으로 지워지지 않은 한 업데이트되지 않을 것이다.
이러한 사유로, 의도한 값이 서버의 기본값과 일치하더라도, 서버에 의해 기본 처리된 특정 필드는 구성 파일 내 명시적으로 정의할 것을 권고한다. 이렇게 하면 서버에 의해 다시 기본 처리되지 않게 될 충돌하는 값을 보다 쉽게 인식할 수 있도록 해준다.
Example:
# last-applied-configuration
spec:
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
# configuration file
spec:
strategy:
type: Recreate # updated value
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
# live configuration
spec:
strategy:
type: RollingUpdate # defaulted value
rollingUpdate: # defaulted value derived from type
maxSurge : 1
maxUnavailable: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
# result after merge - ERROR!
spec:
strategy:
type: Recreate # updated value: incompatible with rollingUpdate
rollingUpdate: # defaulted value: incompatible with "type: Recreate"
maxSurge : 1
maxUnavailable: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
설명:
strategy.type
을 정의하지 않고 디플로이먼트를 생성한다.strategy.type
을 RollingUpdate
로 기본 설정하고
strategy.rollingUpdate
값을 기본 값으로 처리한다.strategy.type
를 Recreate
로 변경한다.
서버에서 해당 값이 삭제될 거라 예상하지만 strategy.rollingUpdate
값은 기본값으로 남아 있다.strategy.rollingUpdate
값이 처음에 구성 파일에서 지정되었다면,
이것을 삭제해야 한다는 것이 더 분명했을 것이다.strategy.rollingUpdate
가 지워지지 않았기 때문에 적용은 실패한다.
strategy.rollingupdate
필드는 Recreate
의 strategy.type
으로 정의될 수 없다.권고: 이들 필드는 오브젝트 구성 파일 내 명시적으로 정의돼야 한다.
구성 파일 내 나타나지 않는 필드는 그 값을
null
로 설정하고 나서 구성 파일을 적용함으로써 지워질 수 있다.
서버가 기본 값을 할당했던 필드에 대해서, 이는 다시 기본 값을
할당하도록 한다.
개별 오브젝트 필드를 변경시키는 데 사용해야 하는 유일한 방법은 다음과 같다.
kubectl apply
를 사용한다.kubectl scale
을 사용한다.구성 파일에 필드를 추가한다. 해당 필드의 경우
kubectl apply
를 거치지 않는 활성 구성에 대해 직접 업데이트를 적용하지 않는다.
쿠버네티스 1.5로부터 구성 파일에서 명령형 작성자로 소유권을 변경하는데 수동 단계 필요하다.
kubectl.kubernetes.io/last-applied-configuration
어노테이션에서 필드를 제거한다.쿠버네티스 오브젝트는 한 번에 오직 하나의 방법을 사용하여 관리돼야 한다. 하나의 방법에서 다른 방법으로 전환하는 것은 가능하나, 수동 프로세스이다.
명령형 커맨드 관리에서 오브젝트 구성으로 이전하는 것은 여러 수동 단계를 포함한다.
활성 오브젝트를 로컬 구성 파일로 내보낸다.
kubectl get <종류>/<이름> -o yaml > <종류>_<이름>.yaml
구성 파일에서 수동으로 status
필드를 제거한다.
`kubectl apply` 구성 파일에 존재한다고 하더라도 상태 필드가 업데이트되지 않기 때문에,
이 단계는 선택적이다.
오브젝트의 kubectl.kubernetes.io/last-applied-configuration
어노테이션을 설정한다.
kubectl replace --save-config -f <종류>_<이름>.yaml
오직 오브젝트를 관리하기 위해 kubectl apply
를 사용하도록 프로세스를 변경한다.
오브젝트의 kubectl.kubernetes.io/last-applied-configuration
어노테이션을 설정한다.
kubectl replace --save-config -f <종류>_<이름>.yaml
오직 오브젝트를 관리하기 위해 kubectl apply
를 사용하도록 프로세스를 변경한다.
권고되는 접근 방법은 다른 의미론적 의미를 가지지 않고 컨트롤러에 의해서만 사용되는 단일, 불변의 파드템플릿 레이블을 정의하는 것이다.
예시:
selector:
matchLabels:
controller-selector: "apps/v1/deployment/nginx"
template:
metadata:
labels:
controller-selector: "apps/v1/deployment/nginx"
Kustomize는 kustomization 파일을 통해 쿠버네티스 오브젝트를 사용자가 원하는 대로 변경하는(customize) 독립형 도구이다.
1.14 이후로, kubectl도 kustomization 파일을 사용한 쿠버네티스 오브젝트의 관리를 지원한다. kustomization 파일을 포함하는 디렉터리 내의 리소스를 보려면 다음 명령어를 실행한다.
kubectl kustomize <kustomization_directory>
이 리소스를 적용하려면 kubectl apply
를 --kustomize
또는 -k
플래그와 함께 실행한다.
kubectl apply -k <kustomization_directory>
kubectl
을 설치한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
Kustomize는 쿠버네티스 구성을 사용자 정의화하는 도구이다. 이는 애플리케이션 구성 파일을 관리하기 위해 다음 기능들을 가진다.
컨피그맵과 시크릿은 파드와 같은 다른 쿠버네티스 오브젝트에서 사용되는 설정이나 민감한 데이터를 가지고 있다. 컨피그맵이나 시크릿의 실질적인 소스는 일반적으로 .properties
파일이나 ssh key 파일과 같은 것들은 클러스터 외부에 있다.
Kustomize는 시크릿과 컨피그맵을 파일이나 문자열에서 생성하는 secretGenerator
와 configMapGenerator
를 가지고 있다.
파일에서 컨피그맵을 생성하려면 configMapGenerator
내의 files
리스트에 항목을 추가한다. 다음은 하나의 .properties
파일에서 데이터 항목으로 컨피그맵을 생성하는 예제이다.
# application.properties 파일을 생성
cat <<EOF >application.properties
FOO=Bar
EOF
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-1
files:
- application.properties
EOF
생성된 컨피그맵은 다음 명령어로 검사할 수 있다.
kubectl kustomize ./
생성된 컨피그맵은 다음과 같다.
apiVersion: v1
data:
application.properties: |
FOO=Bar
kind: ConfigMap
metadata:
name: example-configmap-1-8mbdf7882g
env 파일에서 컨피그맵을 생성하려면, configMapGenerator
의 envs
리스트에 항목을 추가한다. 다음은 .env
파일의 데이터 항목으로 컨피그맵을 생성하는 예시를 보여준다.
# .env 파일 생성
cat <<EOF >.env
FOO=Bar
EOF
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-1
envs:
- .env
EOF
생성된 컨피그맵은 다음 명령어로 검사할 수 있다.
kubectl kustomize ./
생성된 컨피그맵은 다음과 같다.
apiVersion: v1
data:
FOO: Bar
kind: ConfigMap
metadata:
name: example-configmap-1-42cfbf598f
.env
파일의 각 변수는 생성한 컨피그맵에서 분리된 키가 된다. .properties
라는 이름의 파일을 내장하는 이전 예시(그리고 모든 항목들)는 단일 키를 위한 값이므로 이 예시와는 다르다.컨피그맵은 문자로된 키-값 쌍들로도 생성할 수 있다. 문자로된 키-값 쌍에서 컨피그맵을 생성하려면, configMapGenerator 내의 literals
리스트에 항목을 추가한다. 다음은 키-값 쌍을 데이터 항목으로 받는 컨피그맵을 생성하는 예제이다.
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-2
literals:
- FOO=Bar
EOF
생성된 컨피그맵은 다음 명령어로 확인할 수 있다.
kubectl kustomize ./
생성된 컨피그맵은 다음과 같다.
apiVersion: v1
data:
FOO: Bar
kind: ConfigMap
metadata:
name: example-configmap-2-g2hdhfc6tk
디플로이먼트에서 생성된 컨피그맵을 사용하기 위해서는, configMapGenerator의 이름을 참조한다. Kustomize는 자동으로 해당 이름을 생성된 이름으로 교체할 것이다.
다음은 생성된 컨피그맵을 사용하는 디플로이먼트의 예시다.
# application.properties 파일을 생성한다.
cat <<EOF >application.properties
FOO=Bar
EOF
cat <<EOF >deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app
volumeMounts:
- name: config
mountPath: /config
volumes:
- name: config
configMap:
name: example-configmap-1
EOF
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
configMapGenerator:
- name: example-configmap-1
files:
- application.properties
EOF
컨피그맵과 디플로이먼트를 생성한다.
kubectl kustomize ./
생성된 디플로이먼트는 이름을 통해서 생성된 컨피그맵을 참조한다.
apiVersion: v1
data:
application.properties: |
FOO=Bar
kind: ConfigMap
metadata:
name: example-configmap-1-g4hk9g2ff8
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-app
name: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- image: my-app
name: app
volumeMounts:
- mountPath: /config
name: config
volumes:
- configMap:
name: example-configmap-1-g4hk9g2ff8
name: config
파일 또는 문자로된 키-값 쌍들로 시크릿을 생성할 수 있다. 파일에서 시크릿을 생성하려면 secretGenerator
내의 files
리스트에 항목을 추가한다. 다음은 파일을 데이터 항목으로 받는 시크릿을 생성하는 예제이다.
# password.txt 파일을 생성
cat <<EOF >./password.txt
username=admin
password=secret
EOF
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: example-secret-1
files:
- password.txt
EOF
생성된 시크릿은 다음과 같다.
apiVersion: v1
data:
password.txt: dXNlcm5hbWU9YWRtaW4KcGFzc3dvcmQ9c2VjcmV0Cg==
kind: Secret
metadata:
name: example-secret-1-t2kt65hgtb
type: Opaque
문자로된 키-값 쌍으로 시크릿을 생성하려면, secretGenerator
내의 literals
리스트에 항목을 추가한다. 다음은 키-값 쌍을 데이터 항목으로 받는 시크릿을 생성하는 예제이다.
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: example-secret-2
literals:
- username=admin
- password=secret
EOF
생성된 시크릿은 다음과 같다.
apiVersion: v1
data:
password: c2VjcmV0
username: YWRtaW4=
kind: Secret
metadata:
name: example-secret-2-t52t6g96d8
type: Opaque
컨피그맵과 유사하게, 생성된 시크릿도 secretGenerator의 이름을 참조함으로써 디플로이먼트에서 사용될 수 있다.
# password.txt 파일을 생성한다.
cat <<EOF >./password.txt
username=admin
password=secret
EOF
cat <<EOF >deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app
volumeMounts:
- name: password
mountPath: /secrets
volumes:
- name: password
secret:
secretName: example-secret-1
EOF
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
secretGenerator:
- name: example-secret-1
files:
- password.txt
EOF
생성된 컨피그맵과 시크릿은 콘텐츠 해시 접미사가 추가된다. 이는 콘텐츠가 변경될 때 새로운 컨피그맵 이나 시크릿이 생성되는 것을 보장한다. 접미사를 추가하는 동작을 비활성화하는 방법으로 generatorOptions
를 사용할 수 있다. 그밖에, 생성된 컨피그맵과 시크릿에 교차 편집 옵션들을 지정해주는 것도 가능하다.
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-3
literals:
- FOO=Bar
generatorOptions:
disableNameSuffixHash: true
labels:
type: generated
annotations:
note: generated
EOF
생성된 컨피그맵을 보려면 kubectl kustomize ./
를 실행한다.
apiVersion: v1
data:
FOO: Bar
kind: ConfigMap
metadata:
annotations:
note: generated
labels:
type: generated
name: example-configmap-3
프로젝트 내 모든 쿠버네티스 리소스에 교차 편집 필드를 설정하는 것은 꽤나 일반적이다. 교차 편집 필드를 설정하는 몇 가지 사용 사례는 다음과 같다.
다음은 예제이다.
# deployment.yaml을 생성
cat <<EOF >./deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
EOF
cat <<EOF >./kustomization.yaml
namespace: my-namespace
namePrefix: dev-
nameSuffix: "-001"
commonLabels:
app: bingo
commonAnnotations:
oncallPager: 800-555-1212
resources:
- deployment.yaml
EOF
이 필드들이 디플로이먼트 리소스에 모두 설정되었는지 보려면 kubectl kustomize ./
를 실행한다.
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
oncallPager: 800-555-1212
labels:
app: bingo
name: dev-nginx-deployment-001
namespace: my-namespace
spec:
selector:
matchLabels:
app: bingo
template:
metadata:
annotations:
oncallPager: 800-555-1212
labels:
app: bingo
spec:
containers:
- image: nginx
name: nginx
프로젝트 내 리소스의 집합을 구성하여 이들을 동일한 파일이나 디렉터리 내에서 관리하는 것은 일반적이다. Kustomize는 서로 다른 파일들로 리소스를 구성하고 패치나 다른 사용자 정의를 이들에 적용하는 것을 제공한다.
Kustomize는 서로 다른 리소스들의 구성을 지원한다. kustomization.yaml
파일 내 resources
필드는 구성 내에 포함하려는 리소스들의 리스트를 정의한다. resources
리스트 내에 리소스의 구성 파일의 경로를 설정한다.
다음 예제는 디플로이먼트와 서비스로 구성된 NGINX 애플리케이션이다.
# deployment.yaml 파일 생성
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
EOF
# service.yaml 파일 생성
cat <<EOF > service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: my-nginx
EOF
# 이들을 구성하는 kustomization.yaml 생성
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
- service.yaml
EOF
kubectl kustomize ./
의 리소스에는 디플로이먼트와 서비스 오브젝트가 모두 포함되어 있다.
패치는 리소스에 다른 사용자 정의를 적용하는 데 사용할 수 있다. Kustomize는
patchesStrategicMerge
와 patchesJson6902
를 통해 서로 다른 패치 메커니즘을 지원한다. patchesStrategicMerge
는 파일 경로들의 리스트이다. 각각의 파일은 전략적 병합 패치로 분석될 수 있어야 한다. 패치 내부의 네임은 반드시 이미 읽혀진 리소스 네임과 일치해야 한다. 한 가지 일을 하는 작은 패치가 권장된다. 예를 들기 위해 디플로이먼트 레플리카 숫자를 증가시키는 하나의 패치와 메모리 상한을 설정하는 다른 패치를 생성한다.
# deployment.yaml 파일 생성
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
EOF
# increase_replicas.yaml 패치 생성
cat <<EOF > increase_replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 3
EOF
# 다른 패치로 set_memory.yaml 생성
cat <<EOF > set_memory.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
template:
spec:
containers:
- name: my-nginx
resources:
limits:
memory: 512Mi
EOF
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
patchesStrategicMerge:
- increase_replicas.yaml
- set_memory.yaml
EOF
디플로이먼트를 보려면 kubectl kustomize ./
를 실행한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 3
selector:
matchLabels:
run: my-nginx
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- image: nginx
name: my-nginx
ports:
- containerPort: 80
resources:
limits:
memory: 512Mi
모든 리소스 또는 필드가 전략적 병합 패치를 지원하는 것은 아니다. 임의의 리소스 내 임의의 필드의 수정을 지원하기 위해,
Kustomize는 patchesJson6902
를 통한 JSON 패치 적용을 제공한다.
Json 패치의 정확한 리소스를 찾기 위해, 해당 리소스의 group, version, kind, name이
kustomization.yaml
내에 명시될 필요가 있다. 예를 들면, patchesJson6902
를 통해
디플로이먼트 오브젝트의 레플리카 개수를 증가시킬 수 있다.
# deployment.yaml 파일 생성
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
EOF
# json 패치 생성
cat <<EOF > patch.yaml
- op: replace
path: /spec/replicas
value: 3
EOF
# kustomization.yaml 생성
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
patchesJson6902:
- target:
group: apps
version: v1
kind: Deployment
name: my-nginx
path: patch.yaml
EOF
kubectl kustomize ./
를 실행하여 replicas
필드가 갱신되었는지 확인한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 3
selector:
matchLabels:
run: my-nginx
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- image: nginx
name: my-nginx
ports:
- containerPort: 80
패치 기능에 추가로 Kustomize는 패치를 생성하지 않고 컨테이너 이미지를 사용자 정의하거나 다른 오브젝트의 필드 값을 컨테이너에 주입하는
기능도 제공한다. 예를 들어 kustomization.yaml
의 images
필드에 신규 이미지를 지정하여 컨테이너에서 사용되는 이미지를 변경할 수 있다.
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
EOF
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
images:
- name: nginx
newName: my.image.registry/nginx
newTag: 1.4.0
EOF
사용된 이미지가 갱신되었는지 확인하려면 kubectl kustomize ./
를 실행한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 2
selector:
matchLabels:
run: my-nginx
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- image: my.image.registry/nginx:1.4.0
name: my-nginx
ports:
- containerPort: 80
가끔, 파드 내에서 실행되는 애플리케이션이 다른 오브젝트의 설정 값을 사용해야 할 수도 있다. 예를 들어,
디플로이먼트 오브젝트의 파드는 Env 또는 커맨드 인수로 해당 서비스 네임을 읽어야 한다고 하자.
kustomization.yaml
파일에 namePrefix
또는 nameSuffix
가 추가되면 서비스 네임이 변경될 수 있다.
커맨드 인수 내에 서비스 네임을 하드 코딩하는 것을 권장하지 않는다. 이 용도에서 Kustomize는 vars
를 통해 containers에 서비스 네임을 삽입할 수 있다.
# deployment.yaml 파일 생성(문서 구분 기호를 따옴표로 감쌈)
cat <<'EOF' > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
command: ["start", "--host", "$(MY_SERVICE_NAME)"]
EOF
# service.yaml 파일 생성
cat <<EOF > service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: my-nginx
EOF
cat <<EOF >./kustomization.yaml
namePrefix: dev-
nameSuffix: "-001"
resources:
- deployment.yaml
- service.yaml
vars:
- name: MY_SERVICE_NAME
objref:
kind: Service
name: my-nginx
apiVersion: v1
EOF
kubectl kustomize ./
를 실행하면 dev-my-nginx-001
로 컨테이너에 삽입된 서비스 네임을 볼 수 있다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: dev-my-nginx-001
spec:
replicas: 2
selector:
matchLabels:
run: my-nginx
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- command:
- start
- --host
- dev-my-nginx-001
image: nginx
name: my-nginx
Kustomize는 base 와 overlay 의 개념을 가지고 있다. base 는 kustomization.yaml
과 함께 사용되는 디렉터리다. 이는
사용자 정의와 관련된 리소스들의 집합을 포함한다. kustomization.yaml
의 내부에 표시되는 base는 로컬 디렉터리이거나 원격 리포지터리의 디렉터리가
될 수 있다. overlay 는 kustomization.yaml
이 있는 디렉터리로
다른 kustomization 디렉터리들을 bases
로 참조한다. base 는 overlay에 대해서 알지 못하며 여러 overlay들에서 사용될 수 있다.
한 overlay는 다수의 base들을 가질 수 있고, base들에서 모든 리소스를 구성할 수 있으며,
이들의 위에 사용자 정의도 가질 수 있다.
다음은 base에 대한 예이다.
# base를 가지는 디렉터리 생성
mkdir base
# base/deployment.yaml 생성
cat <<EOF > base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
EOF
# base/service.yaml 파일 생성
cat <<EOF > base/service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: my-nginx
EOF
# base/kustomization.yaml 생성
cat <<EOF > base/kustomization.yaml
resources:
- deployment.yaml
- service.yaml
EOF
이 base는 다수의 overlay에서 사용될 수 있다. 다른 namePrefix
또는 다른 교차 편집 필드들을
서로 다른 overlay에 추가할 수 있다. 다음 예제는 동일한 base를 사용하는 두 overlay들이다.
mkdir dev
cat <<EOF > dev/kustomization.yaml
resources:
- ../base
namePrefix: dev-
EOF
mkdir prod
cat <<EOF > prod/kustomization.yaml
resources:
- ../base
namePrefix: prod-
EOF
kustomization.yaml
에서 관리되는 리소스를 인식하려면 kubectl
명령어에 --kustomize
나 -k
를 사용한다.
-k
는 다음과 같이 kustomization 디렉터리를 가리키고 있어야 한다는 것을 주의한다.
kubectl apply -k <kustomization directory>/
다음 kustomization.yaml
이 주어지고,
# deployment.yaml 파일 생성
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
EOF
# kustomization.yaml 생성
cat <<EOF >./kustomization.yaml
namePrefix: dev-
commonLabels:
app: my-nginx
resources:
- deployment.yaml
EOF
디플로이먼트 오브젝트 dev-my-nginx
를 적용하려면 다음 명령어를 실행한다.
> kubectl apply -k ./
deployment.apps/dev-my-nginx created
디플로이먼트 오브젝트 dev-my-nginx
를 보려면 다음 명령어들 중에 하나를 실행한다.
kubectl get -k ./
kubectl describe -k ./
다음 명령을 실행해서 디플로이먼트 오브젝트 dev-my-nginx
를 매니페스트가 적용된 경우의 클러스터 상태와 비교한다.
kubectl diff -k ./
디플로이먼트 오브젝트 dev-my-nginx
를 삭제하려면 다음 명령어를 실행한다.
> kubectl delete -k ./
deployment.apps "dev-my-nginx" deleted
필드 | 유형 | 설명 |
---|---|---|
namespace | string | 모든 리소스에 네임스페이스 추가 |
namePrefix | string | 모든 리소스 네임에 이 필드의 값이 접두사로 추가된다 |
nameSuffix | string | 모든 리소스 네임에 이 필드의 값이 접미사로 추가된다 |
commonLabels | map[string]string | 모든 리소스와 셀렉터에 추가될 레이블 |
commonAnnotations | map[string]string | 모든 리소스에 추가될 어노테이션 |
resources | []string | 이 리스트 내 각각의 항목은 반드시 존재하는 리소스 구성 파일로 해석되어야 한다. |
configMapGenerator | []ConfigMapArgs | 이 리스트의 각 항목은 컨피그맵을 생성한다. |
secretGenerator | []SecretArgs | 이 리스트의 각 항목은 시크릿을 생성한다. |
generatorOptions | GeneratorOptions | 모든 컨피그맵 및 시크릿 생성자(generator)의 동작을 수정한다. |
bases | []string | 이 리스트 내 각각의 항목은 kustomization.yaml 파일을 가지는 디렉터리로 해석되어야 한다. |
patchesStrategicMerge | []string | 이 리스트 내 각각의 항목은 쿠버네티스 오브젝트의 전략적 병합 패치로 해석되어야 한다. |
patchesJson6902 | []Patch | 이 리스트 내 각각의 항목은 쿠버네티스 오브젝트와 Json 패치로 해석되어야 한다. |
vars | []Var | 각각의 항목은 한 리소스의 필드에서 텍스트를 캡쳐한다. |
images | []Image | 각각의 항목은 패치를 생성하지 않고 하나의 이미지에 대한 name, tags 그리고/또는 digest를 수정한다. |
configurations | []string | 이 리스트 내 각각의 항목은 Kustomize 변환 설정을 포함하는 파일로 해석되어야 한다. |
crds | []string | 이 리스트 내 각각의 항목은 쿠버네티스 타입에 대한 OpenAPI 정의 파일로 해석되어야 한다. |
쿠버네티스 오브젝트는 kubectl
커맨드 라인 툴 속에 내장된 명령형 커맨드를 이용함으로써
바로 신속하게 생성, 업데이트 및 삭제할 수 있다. 이 문서는 어떻게 커맨드가 구성되어 있으며,
이를 사용하여 활성 오브젝트를 어떻게 관리하는 지에 대해 설명한다.
kubectl
을 설치한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
kubectl
툴은 3가지 종류의 오브젝트 관리를 지원한다.
각 종류별 오브젝트 관리의 장점과 단점에 대한 논의는 쿠버네티스 오브젝트 관리 를 참고한다.
kubectl
툴은 가장 일반적인 오브젝트 타입을 생성하는데 동사 형태 기반의 커맨드를
지원한다. 쿠버네티스 오브젝트 타입에 익숙하지 않은 사용자가 인지할 수 있도록 커맨드
이름이 지어졌다.
run
: 컨테이너를 실행할 새로운 파드를 생성한다.expose
: 파드에 걸쳐 트래픽을 로드 밸런스하도록 새로운 서비스 오브젝트를 생성한다.autoscale
: 디플로이먼트와 같이, 하나의 컨트롤러에 대해 자동으로 수평적 스케일이 이루어 지도록 새로운 Autoscaler 오브젝트를 생성한다.또한 kubectl
툴은 오브젝트 타입에 의해 구동되는 생성 커맨드를 지원한다.
이러한 커맨드는 더 많은 오브젝트 타입을 지원해주며 그 의도하는 바에 대해
보다 명확하게 해주지만, 사용자가 생성하고자 하는 오브젝트 타입에 대해
알 수 있도록 해야 한다.
create <오브젝트 타입> [<서브 타입>] <인스턴스명>
일부 오브젝트 타입은 create
커맨드 내 정의할 수 있는 서브 타입을 가진다.
예를 들어, 서비스 오브젝트는 ClusterIP, LoadBalancer 및 NodePort 등을
포함하는 여러 서브 타입을 가진다, 다음은 NodePort 서브 타입을 통해 서비스를
생성하는 예제이다.
kubectl create service nodeport <사용자 서비스 명칭>
이전 예제에서, create service nodeport
커맨드는
create service
커맨드의 서브 커맨드라고 칭한다.
-h
플래그를 사용하여 서브 커맨드에 의해 지원되는 인수 및 플래그를
찾아 볼 수 있다.
kubectl create service nodeport -h
kubectl
커맨드는 일반적인 몇몇의 업데이트 작업을 위해 동사 형태 기반의 커맨드를 지원한다.
이 커맨드는 쿠버네티스 오브젝트에 익숙하지 않은 사용자가 설정되어야
하는 특정 필드를 모르는 상태에서도 업데이트를 수행할 수 있도록
이름 지어졌다.
scale
: 컨트롤러의 레플리카 수를 업데이트 함으로써 파드를 추가 또는 제거하는 컨트롤러를 수평적으로 스케일한다.annotate
: 오브젝트로부터 어노테이션을 추가 또는 제거한다.label
: 오브젝트에서 레이블을 추가 또는 제거한다.kubectl
커맨드는 또한 오브젝트 측면에서 구동되는 업데이트 커맨드를 지원한다.
이 측면의 설정은 다른 오브젝트 타입에 대한 다른 필드를 설정 할 수도 있다.
set
<field>
: 오브젝트의 측면을 설정한다.kubectl
툴은 활성 오브젝트를 직접 업데이트하기 위해 추가적인 방법을 지원하지만,
쿠버네티스 오브젝트 스키마에 대한 추가적인 이해를 요구한다.
edit
: 편집기에서 구성을 열어 활성 오브젝트에 대한 원래 그대로의 구성을 바로 편집한다.patch
: 패치 문자열를 사용하여 활성 오브젝트를 바로 편집한다.
패치 문자열에 대한 보다 자세한 정보를 보려면
API 규정에서 패치 섹션을 참고한다.클러스터에서 오브젝트를 삭제하기 위해 delete
커맨드을 사용할 수 있다.
delete <타입>/<이름>
kubectl delete
를 사용할 수
있다. 차이점은 커맨드에 전해지는 인수에 있다. 명령형 커맨드로
kubectl delete
을 사용하기 위해, 삭제할 오브젝트를 인수로 전한다.
다음은 nginx라는 디플로이먼트 오브젝트를 전하는 예제이다.kubectl delete deployment/nginx
오브젝트에 대한 정보를 출력하는 몇 가지 커맨드가 있다.
get
: 일치하는 오브젝트에 대한 기본 정보를 출력한다. 옵션 리스트를 확인하기 위해 get -h
를 사용한다.describe
: 일치하는 오브젝트에 대해 수집한 상세한 정보를 출력한다.logs
: 파드에서 실행 중인 컨테이너에 대한 stdout과 stderr를 출력한다.set
커맨드 사용하기create
커맨드에 사용할 수 있는 플래그가 없는 몇 가지 오브젝트
필드가 있다. 이러한 경우, 오브젝트 생성 전에 필드에 대한 값을
정의하기 위해 set
과 create
을 조합해서 사용할 수 있다.
이는 set
커맨드에 create
커맨드의 출력을 파이프 함으로써 수행할 수 있다.
다음은 관련 예제이다.
kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run=client | kubectl set selector --local -f - 'environment=qa' -o yaml | kubectl create -f -
kubectl create service -o yaml --dry-run=client
커맨드는 서비스에 대한 구성을 생성하지만, 이를 쿠버네티스 API 서버에 전송하는 대신 YAML 형식으로 stdout에 출력한다.kubectl set selector --local -f - -o yaml
커맨드는 stdin으로부터 구성을 읽어, YAML 형식으로 stdout에 업데이트된 구성을 기록한다.kubectl create -f -
커맨드는 stdin을 통해 제공된 구성을 사용하여 오브젝트를 생성한다.--edit
사용하기생성 전에 오브젝트에 임의의 변경을 가하기 위해 kubectl create --edit
을 사용할 수 있다.
다음은 관련 예제이다.
kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run=client > /tmp/srv.yaml
kubectl create --edit -f /tmp/srv.yaml
kubectl create service
커맨드는 서비스에 대한 구성을 생성하고 이를 /tmp/srv.yaml
에 저장한다.kubectl create --edit
커맨드는 오브젝트를 생성하기 전에 편집을 위해 구성 파일을 열어준다.쿠버네티스 오브젝트는 YAML 또는 JSON으로 작성된 오프젝트 구성 파일과 함께 kubectl
커맨드 라인 툴을 이용하여 생성, 업데이트 및 삭제할 수 있다.
이 문서는 구성 파일을 이용하여 어떻게 오브젝트를 정의하고 관리할 수 있는지에 대해 설명한다.
kubectl
을 설치한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
kubectl
툴은 3가지 종류의 오브젝트 관리를 지원한다.
각 종류별 오브젝트 관리의 장점과 단점에 대한 논의는 쿠버네티스 오브젝트 관리를 참고한다.
구성 파일로부터 오브젝트를 생성하기 위해 kubectl create -f
를 사용할 수 있다.
보다 상세한 정보는 쿠버네티스 API 참조를
참조한다.
kubectl create -f <파일명|url>
replace
커맨드로 오브젝트를 업데이트 하게되면,
구성 파일에 정의되지 않은 스펙의 모든 부분이 삭제된다. 이는
externalIPs
필드가 구성 파일로부터 독립적으로 관리되는
LoadBalancer
타입의 서비스와 같이, 클러스터 의해 부분적으로
관리되는 스펙의 오브젝트와 함께 사용되어서는 안된다.
독립적으로 관리되는 필드는 replace
로 삭제되는 것을 방지하기 위해
구성 파일에 복사되어져야만 한다.구성 파일에 따라 활성 오브젝트를 업데이트하기 위해 kubectl replace -f
를 사용할 수 있다.
kubectl replace -f <파일명|url>
구성 파일에 정의한 오브젝트를 삭제하기 위해 kubectl delete -f
를
사용할 수 있다.
kubectl delete -f <파일명|url>
구성 파일이 metadata
섹션에서 name
필드 대신 generateName
필드를 지정한 경우, kubectl delete -f <filename|url>
을 사용하여
오브젝트를 삭제할 수 없다.
오브젝트를 삭제하려면 다른 플래그를 사용해야 한다. 예를 들면, 다음과 같다.
kubectl delete <type> <name>
kubectl delete <type> -l <label>
구성 파일에 정의한 오브젝트에 관한 정보 확인을 위해 kubectl get -f
명령을 사용할 수 있다.
kubectl get -f <파일명|url> -o yaml
-o yaml
플래그는 전체 오브젝트 구성이 출력되도록 정의한다. 옵션의 리스트를 확인하기
위해서는 kubectl get -h
를 사용한다.
create
, replace
, 그리고 delete
명령은 각 오브젝트의 구성이
그 구성 파일 내에 완전하게 정의되고 기록되어질 경우 잘 동작한다.
그러나 활성 오브젝트가 업데이트 되고, 구성 파일 안에 병합되지 않으면,
업데이트 내용은 다음번 replace
가 실행될 때 삭제될 것이다.
이는 HorizontalPodAutoscaler와 같은 컨트롤러가
활성 오브젝트를 직접적으로 업데이트하도록 할 경우 발생한다.
여기 예시가 있다.
동일 오브젝트에 대해 여러 명의 작성자들로부터의 지원이 필요한 경우, 오브젝트를 관리하기 위해
kubectl apply
를 사용할 수 있다.
구성 파일에 대한 URL을 가진다고 가정해보자.
kubectl create --edit
을 사용하여 오브젝트가 생성되기 전에
구성을 변경할 수 있다. 이는 독자가 수정할 수 있는 구성 파일을
가르키는 튜토리얼과 작업에 특히 유용하다.
kubectl create -f <url> --edit
령형 커맨드에서 명령형 오브젝트 구성으로 전환하기 위해 몇 가지 수동 단계를 포함한다.
다음과 같이 활성 오브젝트를 로컬 오브젝트 구성 파일로 내보낸다.
kubectl get <종류>/<이름> -o yaml > <종류>_<이름>.yaml
수동으로 오브젝트 구성 파일에서 상태 필드를 제거한다.
이후 오브젝트 관리를 위해, replace
만 사용한다.
kubectl replace -f <종류>_<이름>.yaml
권고되는 접근방법은 다른 의미론적 의미가 없는 컨트롤러 셀렉터의 의해서만 사용되는 단일, 불변의 PodTemplate 레이블로 정의하는 것이다.
레이블 예시:
selector:
matchLabels:
controller-selector: "apps/v1/deployment/nginx"
template:
metadata:
labels:
controller-selector: "apps/v1/deployment/nginx"
이 페이지는 kubectl
커맨드라인 툴을 이용하여 쿠버네티스
시크릿을
생성, 편집, 관리, 삭제하는 방법을 보여준다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
시크릿
오브젝트는 파드가 서비스에 접근하기 위해 사용하는 자격 증명과 같은
민감한 데이터를 저장한다. 예를 들어 데이터베이스에 접근하는데 필요한 사용자 이름과 비밀번호를
저장하기 위해서 시크릿이 필요할 수 있다.
명령어를 통해 원시 데이터를 바로 보내거나, 파일에 자격 증명을 저장하고 명령어로 전달하는 방식으로
시크릿을 생성할 수 있다. 다음 명령어는 사용자 이름을 admin
으로
비밀번호는 S!B\*d$zDsb=
으로 저장하는 시크릿을 생성한다.
다음 명령어를 실행한다.
kubectl create secret generic db-user-pass \
--from-literal=username=admin \
--from-literal=password='S!B\*d$zDsb='
문자열에서 $
, \
, *
, =
및 !
과 같은 특수 문자를 이스케이프(escape)하기
위해서는 작은따옴표 ''
를 사용해야 한다. 그렇지 않으면 셸은 이런 문자들을
해석한다.
base64로 인코딩된 자격 증명의 값들을 파일에 저장한다.
echo -n 'admin' | base64 > ./username.txt
echo -n 'S!B\*d$zDsb=' | base64 > ./password.txt
-n
플래그는 생성된 파일이 텍스트 끝에 추가적인 개행 문자를 갖지
않도록 보장한다. 이는 kubectl
이 파일을 읽고 내용을 base64
문자열로 인코딩할 때 개행 문자도 함께 인코딩될 수 있기 때문에
중요하다. 파일에 포함된 문자열에서 특수 문자를 이스케이프 할
필요는 없다.
kubectl
명령어에 파일 경로를 전달한다.
kubectl create secret generic db-user-pass \
--from-file=./username.txt \
--from-file=./password.txt
기본 키 이름은 파일 이름이다. 선택적으로 --from-file=[key=]source
를 사용하여
키 이름을 설정할 수 있다. 예제:
kubectl create secret generic db-user-pass \
--from-file=username=./username.txt \
--from-file=password=./password.txt
두 방법 모두 출력은 다음과 유사하다.
secret/db-user-pass created
시크릿이 생성되었는지 확인한다.
kubectl get secrets
출력은 다음과 유사하다.
NAME TYPE DATA AGE
db-user-pass Opaque 2 51s
시크릿의 상세 사항을 보자.
kubectl describe secret db-user-pass
출력은 다음과 유사하다.
Name: db-user-pass
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 12 bytes
username: 5 bytes
kubectl get
및 kubectl describe
명령은
기본적으로 시크릿
의 내용을 표시하지 않는다. 이는 시크릿
이 실수로 노출되거나
터미널 로그에 저장되는 것을 방지하기 위한 것이다.
생성한 시크릿을 보려면 다음 명령을 실행한다.
kubectl get secret db-user-pass -o jsonpath='{.data}'
출력은 다음과 유사하다.
{ "password": "UyFCXCpkJHpEc2I9", "username": "YWRtaW4=" }
password
데이터를 디코딩한다.
echo 'UyFCXCpkJHpEc2I9' | base64 --decode
출력은 다음과 유사하다.
S!B\*d$zDsb=
이 예시는 문서화를 위한 것이다. 실제로,
이 방법은 인코딩된 데이터가 포함된 명령어를 셸 히스토리에 남기게 되는 문제를 야기할 수 있다.
당신의 컴퓨터에 접근할 수 있는 사람은 누구나 그 명령어를 찾아 그 비밀 정보를
디코드할 수 있다. 더 나은 접근법은 시크릿을 보는 명령어와 디코드하는 명령어를
조합하여 사용하는 것이다.
kubectl get secret db-user-pass -o jsonpath='{.data.password}' | base64 --decode
존재하는 시크릿
오브젝트가 수정 불가능한(immutable)이
아니라면 편집할 수 있다. 시크릿을 편집하기 위해서
다음 명령어를 실행한다.
kubectl edit secrets <secret-name>
이 명령어는 기본 편집기를 열고 다음 예시와 같이 data
필드의 base64로 인코딩된
시크릿의 값들을 업데이트할 수 있도록 허용한다.
# 아래 오브젝트를 편집하길 바란다. '#'로 시작하는 줄은 무시될 것이고,
# 빈 파일은 편집을 중단시킬 것이다. 이 파일을 저장하는 동안 오류가 발생한다면
# 이 파일은 관련된 오류와 함께 다시 열린다.
#
apiVersion: v1
data:
password: UyFCXCpkJHpEc2I9
username: YWRtaW4=
kind: Secret
metadata:
creationTimestamp: "2022-06-28T17:44:13Z"
name: db-user-pass
namespace: default
resourceVersion: "12708504"
uid: 91becd59-78fa-4c85-823f-6d44436242ac
type: Opaque
시크릿을 삭제하기 위해서 다음 명령어를 실행한다.
kubectl delete secret db-user-pass
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
먼저 매니페스트에 JSON 이나 YAML 형식으로 시크릿(Secret)
오브젝트를 정의하고,
그 다음 해당 오브젝트를 만든다. 이
시크릿
리소스에는 data
와 stringData
의 두 가지 맵이 포함되어 있다.
data
필드는 base64로 인코딩된 임의의 데이터를 기입하는 데 사용된다.
stringData
필드는 편의를 위해 제공되며, 이를 사용해 같은 데이터를 인코딩되지 않은 문자열로
기입할 수 있다.
data
및 stringData
은 영숫자,
-
, _
그리고 .
로 구성되어야 한다.
다음 예는 data
필드를 사용하여 시크릿에 두 개의 문자열을 저장한다.
문자열을 base64로 변환한다.
echo -n 'admin' | base64
echo -n '1f2d1e2e67df' | base64
base64
도구를 사용할 경우, 사용자는 긴 줄을 분할하는 -b
옵션을 사용해서는 안 된다. 반대로, 리눅스 사용자는 -w
옵션을 사용할 수 없는 경우 base64
명령어 또는 base64 | tr -d '\n'
파이프라인에 -w 0
옵션을 추가해야 한다.출력은 다음과 유사하다.
YWRtaW4=
MWYyZDFlMmU2N2Rm
매니페스트를 생성한다.
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
시크릿(Secret) 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.
kubectl apply
를 사용하여 시크릿(Secret) 생성하기
kubectl apply -f ./secret.yaml
출력은 다음과 유사하다.
secret/mysecret created
시크릿(Secret) 생성과 시크릿(Secret) 데이터 디코딩을 확인하려면, kubectl을 사용한 시크릿 관리을 참조하자.
특정 시나리오의 경우 stringData
필드를 대신 사용할 수 있다. 이
필드를 사용하면 base64로 인코딩되지 않은 문자열을 시크릿에 직접 넣을 수 있으며,
시크릿이 생성되거나 업데이트될 때 문자열이 인코딩된다.
이에 대한 실제적인 예로, 시크릿을 사용하여 구성 파일을 저장하는 애플리케이션을 배포하면서, 배포 프로세스 중에 해당 구성 파일의 일부를 채우려는 경우를 들 수 있다.
예를 들어 애플리케이션에서 다음 구성 파일을 사용하는 경우:
apiUrl: "https://my.api.com/api/v1"
username: "<user>"
password: "<password>"
다음 정의를 사용하여 이를 시크릿에 저장할 수 있다.
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
stringData:
config.yaml: |
apiUrl: "https://my.api.com/api/v1"
username: <user>
password: <password>
시크릿(Secret) 데이터를 검색할 때, 검색 명령은 인코딩된 값을 반환하며,
stringData
에 기입한 일반 텍스트 값이 아닙니다.
예를 들어, 다음 명령을 실행한다면
kubectl apply -f ./secret.yaml
출력은 다음과 유사하다.
apiVersion: v1
data:
config.yaml: YXBpVXJsOiAiaHR0cHM6Ly9teS5hcGkuY29tL2FwaS92MSIKdXNlcm5hbWU6IHt7dXNlcm5hbWV9fQpwYXNzd29yZDoge3twYXNzd29yZH19
kind: Secret
metadata:
creationTimestamp: 2018-11-15T20:40:59Z
name: mysecret
namespace: default
resourceVersion: "7225"
uid: c280ad2e-e916-11e8-98f2-025000000001
type: Opaque
data
와 stringData
모두 명시data
와 stringData
모두에 필드를 명시하면, stringData
에 명시된 값이 사용된다.
예를 들어 다음과 같은 시크릿인 경우,
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
stringData:
username: administrator
다음과 같이 시크릿
오브젝트가 생성됐다.
apiVersion: v1
data:
username: YWRtaW5pc3RyYXRvcg==
kind: Secret
metadata:
creationTimestamp: 2018-11-15T20:46:46Z
name: mysecret
namespace: default
resourceVersion: "7579"
uid: 91460ecb-e917-11e8-98f2-025000000001
type: Opaque
YWRtaW5pc3RyYXRvcg==
는 administrator
으로 디코딩된다.
매니페스트를 사용하여 생성한 시크릿의 데이터를 편집하려면, 매니페스트에서 data
및 stringData
필드를 수정하고 해당 파일을 클러스터에 적용하면 된다.
그렇지 않은 경우 기존의 시크릿
오브젝트를 편집할 수 있다.
수정 불가능한(immutable).
예를 들어, 이전 예제에서 패스워드를 birdsarentreal
로 변경하려면,
다음과 같이 수행한다.
새로운 암호 문자열을 인코딩한다.
echo -n 'birdsarentreal' | base64
출력은 다음과 같다.
YmlyZHNhcmVudHJlYWw=
새 암호 문자열로 data
필드를 업데이트 한다.
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: YmlyZHNhcmVudHJlYWw=
클러스터에 매니페스트를 적용한다.
kubectl apply -f ./secret.yaml
출력은 다음과 같다.
secret/mysecret configured
쿠버네티스는 기존 시크릿
오브젝트를 업데이트한다. 자세히 보면, kubectl
도구는
동일한 이름을 가진 기존 Secret
오브젝트가 있음을 알아낸다.
kubectl
은 기존의 오브젝트를 가져오고, 변경을 계획하며,
변경된 Secret
오브젝트를 클러스터 컨트롤 플레인에 제출한다.
kubectl apply --server-side
를 지정한 경우, kubectl
은
서버 사이드 어플라이를 대신 사용한다.
생성한 시크릿을 삭제하려면 다음 명령을 실행한다.
kubectl delete secret mysecret
kubectl
은 시크릿과 컨피그맵(ConfigMap)을 관리하기위해 Kustomize를 이용한 쿠버네티스 오브젝트의 선언형 관리를
지원한다. Kustomize를 이용하여 리소스 생성기를 생성한다. 이는 kubectl
을
사용하여 API 서버에 적용할 수 있는 시크릿을 생성한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
kustomization.yaml
파일에 다른 기존 파일, .env
파일 및
리터럴(literal) 값들을 참조하는 secretGenerator
를 정의하여 시크릿을 생성할 수 있다.
예를 들어 다음 명령어는 사용자 이름 admin
과 비밀번호 1f2d1e2e67df
를 위해 Kustomization 파일을 생성한다.
secretGenerator:
- name: database-creds
literals:
- username=admin
- password=1f2d1e2e67df
base64로 인코딩된 자격 증명의 값들을 파일에 저장한다.
echo -n 'admin' > ./username.txt
echo -n '1f2d1e2e67df' > ./password.txt
-n
플래그는 파일의 끝에 개행 문자가 존재하지 않는 것을
보장한다.
kustomization.yaml
파일 생성:
secretGenerator:
- name: database-creds
files:
- username.txt
- password.txt
kustomization.yaml
파일에 .env
파일을 명시하여 시크릿 생성자를
정의할 수도 있다. 예를 들어 다음 kustomization.yaml
파일은
.env.secret
파일에서 데이터를 가져온다.
secretGenerator:
- name: db-user-pass
envs:
- .env.secret
모든 경우에 대해, 값을 base64로 인코딩하지 않아도 된다. YAML 파일의 이름은
무조건 kustomization.yaml
또는 kustomization.yml
이어야 한다.
시크릿을 생성하기 위해서 kustomization 파일을 포함하는 디렉토리에 적용한다.
kubectl apply -k <directory-path>
출력은 다음과 유사하다.
secret/database-creds-5hdh7hhgfk created
시크릿이 생성되면 시크릿 데이터를 해싱하고 이름에 해시 값을 추가하여 시크릿 이름이 생성된다. 이렇게 함으로써 데이터가 수정될 때마다 시크릿이 새롭게 생성된다.
시크릿이 생성되었는지 확인하고 시크릿 데이터를 디코딩하려면, 다음을 참조한다. kubectl을 사용한 시크릿 관리.
kustomization.yaml
파일에서 password
와 같은 데이터를 수정한다.
kustomization 파일을 포함하는 디렉토리에 적용한다:
kubectl apply -k <directory-path>
출력은 다음과 유사하다.
secret/db-user-pass-6f24b56cc8 created
편집된 시크릿은 존재하는 Secret
오브젝트를 업데이트하는 것이 아니라
새로운 Secret
오브젝트로 생성된다. 따라서 파드에서 시크릿에 대한 참조를
업데이트해야 한다.
시크릿을 삭제하려면 kubectl
을 사용한다.
kubectl delete secret db-user-pass
본 페이지는 파드 안에서 컨테이너를 실행할 때 커맨드와 인자를 정의하는 방법에 대해 설명한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
파드를 생성할 때, 파드 안에서 동작하는 컨테이너를 위한 커맨드와 인자를
정의할 수 있다. 커맨드를 정의하기 위해서는, 파드 안에서 실행되는 컨테이너에
command
필드를 포함시킨다. 커맨드에 대한 인자를 정의하기 위해서는, 구성
파일에 args
필드를 포함시킨다. 정의한 커맨드와 인자들은 파드가 생성되고
난 이후에는 변경될 수 없다.
구성 파일 안에서 정의하는 커맨드와 인자들은 컨테이너 이미지가 제공하는 기본 커맨드와 인자들보다 우선시 된다. 만약 인자들을 정의하고 커맨드를 정의하지 않는다면, 기본 커맨드가 새로운 인자와 함께 사용된다.
command
필드는 일부 컨테이너 런타임에서 entrypoint
에 해당된다.이 예제에서는 한 개의 컨테이너를 실행하는 파드를 생성한다. 파드를 위한 구성 파일에서 커맨드와 두 개의 인자를 정의한다.
apiVersion: v1
kind: Pod
metadata:
name: command-demo
labels:
purpose: demonstrate-command
spec:
containers:
- name: command-demo-container
image: debian
command: ["printenv"]
args: ["HOSTNAME", "KUBERNETES_PORT"]
restartPolicy: OnFailure
YAML 구성 파일을 활용해 파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/commands.yaml
실행 중인 파드들의 목록을 조회한다.
kubectl get pods
command-demo라는 파드 안에서 실행된 컨테이너가 완료되었다고 출력될 것이다.
컨테이너 안에서 실행된 커맨드의 출력을 보기 위해, 파드의 로그를 확인한다.
kubectl logs command-demo
HOSTNAME과 KUBERNETES_PORT 환경 변수들의 값들이 출력될 것이다.
command-demo
tcp://10.3.240.1:443
이전 예제에서는, 문자열을 제공하면서 직접 인자를 정의해보았다. 문자열을 직접 제공하는 것에 대한 대안으로, 환경 변수들을 사용하여 인자들을 정의할 수 있다.
env:
- name: MESSAGE
value: "hello world"
command: ["/bin/echo"]
args: ["$(MESSAGE)"]
이것은 컨피그 맵과 시크릿을 포함해, 환경 변수를 정의하는데 활용할 수 있는 모든 방법들을 활용해서 파드를 위한 인자를 정의할 수 있다는 것을 의미한다.
"$(VAR)"
와 같이 괄호 안에 나타난다. 이는 변수가 command
나 args
필드 안에서 전개되기 위해 필요한 것이다.일부 경우들에서는 커맨드를 셸 안에서 실행해야할 필요가 있다. 예를 들어, 실행할 커맨드가 서로 연결되어 있는 여러 개의 커맨드들로 구성되어 있거나, 셸 스크립트일 수도 있다. 셸 안에서 커맨드를 실행하려고 한다면, 이런 방식으로 감싸주면 된다.
command: ["/bin/sh"]
args: ["-c", "while true; do echo hello; sleep 10;done"]
본 페이지는 쿠버네티스 파드의 컨테이너를 위한 종속 환경 변수를 정의하는 방법에 대해 설명한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
파드를 생성할 때, 파드 안에서 동작하는 컨테이너를 위한 종속 환경 변수를 설정할 수 있다. 종속 환경 변수를 설정하려면, 구성 파일에서 env
의 value
에 $(VAR_NAME)을 사용한다.
이 예제에서는 한 개의 컨테이너를 실행하는 파드를 생성한다. 파드를 위한 구성 파일은 일반적인 방식으로 정의된 종속 환경 변수를 정의한다. 다음은 파드를 위한 구성 매니페스트 예시이다.
apiVersion: v1
kind: Pod
metadata:
name: dependent-envars-demo
spec:
containers:
- name: dependent-envars-demo
args:
- while true; do echo -en '\n'; printf UNCHANGED_REFERENCE=$UNCHANGED_REFERENCE'\n'; printf SERVICE_ADDRESS=$SERVICE_ADDRESS'\n';printf ESCAPED_REFERENCE=$ESCAPED_REFERENCE'\n'; sleep 30; done;
command:
- sh
- -c
image: busybox:1.28
env:
- name: SERVICE_PORT
value: "80"
- name: SERVICE_IP
value: "172.17.0.1"
- name: UNCHANGED_REFERENCE
value: "$(PROTOCOL)://$(SERVICE_IP):$(SERVICE_PORT)"
- name: PROTOCOL
value: "https"
- name: SERVICE_ADDRESS
value: "$(PROTOCOL)://$(SERVICE_IP):$(SERVICE_PORT)"
- name: ESCAPED_REFERENCE
value: "$$(PROTOCOL)://$(SERVICE_IP):$(SERVICE_PORT)"
YAML 구성 파일을 활용해 파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/inject/dependent-envars.yaml
pod/dependent-envars-demo created
실행 중인 파드의 목록을 조회한다.
kubectl get pods dependent-envars-demo
NAME READY STATUS RESTARTS AGE
dependent-envars-demo 1/1 Running 0 9s
파드 안에서 동작 중인 컨테이너의 로그를 확인한다.
kubectl logs pod/dependent-envars-demo
UNCHANGED_REFERENCE=$(PROTOCOL)://172.17.0.1:80
SERVICE_ADDRESS=https://172.17.0.1:80
ESCAPED_REFERENCE=$(PROTOCOL)://172.17.0.1:80
위에서 보듯이, SERVICE_ADDRESS
는 올바른 종속성 참조, UNCHANGED_REFERENCE
는 잘못된 종속성 참조를 정의했으며 ESCAPED_REFERENCE
는 종속성 참조를 건너뛴다.
미리 정의된 환경 변수를 참조할 때는,
SERVICE_ADDRESS
의 경우와 같이 참조를 올바르게 해석할 수 있다.
env
목록 안에서의 순서가 영향을 준다는 것을 주의하자.
목록에서 더 아래쪽에 명시된 환경 변수는, "정의된" 것으로 보지 않는다.
이것이 바로 위의 예시에서 UNCHANGED_REFERENCE
가 $(PROTOCOL)
을 해석하지 못한 이유이다.
환경 변수가 정의되지 않았거나 일부 변수만 포함된 경우, 정의되지 않은 환경 변수는 UNCHANGED_REFERENCE
의 경우와 같이 일반 문자열로 처리된다. 일반적으로 환경 변수 해석에 실패하더라도 컨테이너의 시작을 막지는 않는다.
$(VAR_NAME)
구문은 이중 $로 이스케이프될 수 있다. (예: $$(VAR_NAME)
)
이스케이프된 참조는 참조된 변수가 정의되었는지 여부에 관계없이 해석을 수행하지 않는다.
이는 위의 ESCAPED_REFERENCE
를 통해 확인할 수 있다.
본 페이지는 쿠버네티스 파드의 컨테이너를 위한 환경 변수를 정의하는 방법에 대해 설명한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
파드를 생성할 때, 파드 안에서 동작하는 컨테이너를 위한 환경 변수를 설정할
수 있다. 환경 변수를 설정하려면, 구성 파일에 env
나 envFrom
필드를
포함시켜야 한다.
이 예제에서, 한 개의 컨테이너를 실행하는 파드를 생성한다. 파드를 위한 구성
파일은 DEMO_GREETING
이라는 이름과 "Hello from the environment"
이라는
값을 가지는 환경 변수를 정의한다. 다음은 파드를 위한 구성 매니페스트
예시이다.
apiVersion: v1
kind: Pod
metadata:
name: envar-demo
labels:
purpose: demonstrate-envars
spec:
containers:
- name: envar-demo-container
image: gcr.io/google-samples/node-hello:1.0
env:
- name: DEMO_GREETING
value: "Hello from the environment"
- name: DEMO_FAREWELL
value: "Such a sweet sorrow"
YAML 구성 파일을 활용해 파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/inject/envars.yaml
실행 중인 파드들의 목록을 조회한다.
kubectl get pods -l purpose=demonstrate-envars
결과는 다음과 같다.
NAME READY STATUS RESTARTS AGE
envar-demo 1/1 Running 0 9s
파드의 컨테이너 환경 변수를 나열한다.
kubectl exec envar-demo -- printenv
결과는 다음과 같다.
NODE_VERSION=4.4.2
EXAMPLE_SERVICE_PORT_8080_TCP_ADDR=10.3.245.237
HOSTNAME=envar-demo
...
DEMO_GREETING=Hello from the environment
DEMO_FAREWELL=Such a sweet sorrow
env
나 envFrom
필드를 이용해 설정된 환경 변수들은 컨테이너 이미지
안에서 명시된 모든 환경 변수들을 오버라이딩한다.파드의 구성 파일 안에서 정의한 환경 변수는
파드의 컨테이너를 위해 설정하는 커맨드와 인자들과 같이,
구성 파일 안의 다른 곳에서 사용할 수 있다.
아래의 구성 파일 예시에서, GREETING
, HONORIFIC
, 그리고
NAME
환경 변수들이 각각 Warm greetings to
, The Most honorable
,
그리고 Kubernetes
로 설정되어 있다. 이 환경 변수들은
이후 env-print-demo
컨테이너에 전달되어 CLI 인자에서
사용된다.
apiVersion: v1
kind: Pod
metadata:
name: print-greeting
spec:
containers:
- name: env-print-demo
image: bash
env:
- name: GREETING
value: "Warm greetings to"
- name: HONORIFIC
value: "The Most Honorable"
- name: NAME
value: "Kubernetes"
command: ["echo"]
args: ["$(GREETING) $(HONORIFIC) $(NAME)"]
컨테이너가 생성되면, echo Warm greetings to The Most Honorable Kubernetes
커맨드가 컨테이너에서 실행된다.
본 페이지는 파드가 환경 변수를 사용하여 downward API 로 파드 내부의 컨테이너 정보를 노출하는 방법에 대해 설명한다. 환경 변수를 사용하여 파드 필드, 컨테이너 필드 또는 둘 다 노출할 수 있다.
쿠버네티스에는 실행 중인 컨테이너에 파드 필드 및 컨테이너 필드를 노출하는 두 가지 방법이 있다.
파드 및 컨테이너 필드를 노출하는 이 두 가지 방법을 downward API라고 한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
이 연습에서 하나의 컨테이너가 있는 파드와 파드 수준의 필드를 실행 중인 컨테이너의 환경변수로 생성한다.
apiVersion: v1
kind: Pod
metadata:
name: dapi-envars-fieldref
spec:
containers:
- name: test-container
image: registry.k8s.io/busybox
command: [ "sh", "-c"]
args:
- while true; do
echo -en '\n';
printenv MY_NODE_NAME MY_POD_NAME MY_POD_NAMESPACE;
printenv MY_POD_IP MY_POD_SERVICE_ACCOUNT;
sleep 10;
done;
env:
- name: MY_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: MY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: MY_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: MY_POD_SERVICE_ACCOUNT
valueFrom:
fieldRef:
fieldPath: spec.serviceAccountName
restartPolicy: Never
매니페스트에서 5개의 환경 변수를 확인할 수 있다. env
필드는
환경 변수 정의의 배열이다. 배열의 첫 번째 요소는 MY_NODE_NAME
환경 변수가 파드의 spec.nodeName
필드에서 값을 가져오도록 지정한다. 마찬가지로 다른 환경 변수도 파드 필드에서 이름을 가져온다.
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/inject/dapi-envars-pod.yaml
파드의 컨테이너가 실행중인지 확인한다.
# 새 파드가 아직 정상 상태가 아니면, 이 명령을 몇 번 다시 실행한다.
kubectl get pods
컨테이너의 로그를 본다.
kubectl logs dapi-envars-fieldref
출력은 선택된 환경 변수의 값을 보여준다.
minikube
dapi-envars-fieldref
default
172.17.0.4
default
이러한 값이 로그에 출력된 이유를 보려면 구성 파일의 command
및 args
필드를 확인하자.
컨테이너가 시작되면 5개의 환경 변수 값을 stdout에 쓰며 10초마다 이를 반복한다.
다음으로 파드에서 실행 중인 컨테이너의 셸을 가져오자.
kubectl exec -it dapi-envars-fieldref -- sh
셸에서 환경 변수를 보자.
# 컨테이너 내부의 쉘에서 실행하자.
printenv
출력은 특정 환경 변수에 파드 필드 값이 할당되었음을 보여준다.
MY_POD_SERVICE_ACCOUNT=default
...
MY_POD_NAMESPACE=default
MY_POD_IP=172.17.0.4
...
MY_NODE_NAME=minikube
...
MY_POD_NAME=dapi-envars-fieldref
이전 연습에서 파드 수준 필드의 정보를 환경 변수의 값으로 사용했다. 이번 연습에서는 파드 전체가 아닌 특정 컨테이너의 일부 필드만 전달한다. 다음은 하나의 컨테이너가 있는 파드의 구성 파일이다.
여기, 다시 하나의 컨테이너만 가진 파드를 위한 매니페스트가 있다.
apiVersion: v1
kind: Pod
metadata:
name: dapi-envars-resourcefieldref
spec:
containers:
- name: test-container
image: registry.k8s.io/busybox:1.24
command: [ "sh", "-c"]
args:
- while true; do
echo -en '\n';
printenv MY_CPU_REQUEST MY_CPU_LIMIT;
printenv MY_MEM_REQUEST MY_MEM_LIMIT;
sleep 10;
done;
resources:
requests:
memory: "32Mi"
cpu: "125m"
limits:
memory: "64Mi"
cpu: "250m"
env:
- name: MY_CPU_REQUEST
valueFrom:
resourceFieldRef:
containerName: test-container
resource: requests.cpu
- name: MY_CPU_LIMIT
valueFrom:
resourceFieldRef:
containerName: test-container
resource: limits.cpu
- name: MY_MEM_REQUEST
valueFrom:
resourceFieldRef:
containerName: test-container
resource: requests.memory
- name: MY_MEM_LIMIT
valueFrom:
resourceFieldRef:
containerName: test-container
resource: limits.memory
restartPolicy: Never
매니페스트에서 4개의 환경 변수를 확인할 수 있다. env
필드는
환경 변수 정의의 배열이다. 배열의 첫 번째 요소는 MY_CPU_REQUEST
환경 변수가 test-container
라는 컨테이너의
requests.cpu
필드에서 값을 가져오도록 지정한다. 마찬가지로 다른 환경 변수도 특정 컨테이너 필드에서
값을 가져온다.
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/inject/dapi-envars-container.yaml
파드의 컨테이너가 실행중인지 확인한다.
# 새 파드가 아직 정상 상태가 아니면, 이 명령을 몇 번 다시 실행한다.
kubectl get pods
컨테이너의 로그를 본다.
kubectl logs dapi-envars-resourcefieldref
출력은 선택된 환경 변수의 값을 보여준다.
1
1
33554432
67108864
spec
을 읽어보자.
파드에 대한 API 정의다. 여기에는 컨테이너 (파드의 일부)의 정의가 포함되어 있다.레거시 API 레퍼런스에서 파드, 컨테이너 및 환경 변수에 대해 읽어본다.
본 페이지는 파드가
downwardAPI
볼륨을 사용하여
파드에서 실행되는 컨테이너에 자신에 대한 정보를 노출하는 방법에 대해 설명한다.
downwardAPI
볼륨은 파드 필드와 컨테이너 필드를 노출할 수 있다.
쿠버네티스에는 실행 중인 컨테이너에 파드 필드 및 컨테이너 필드를 노출하는 두 가지 방법이 있다.
파드 및 컨테이너 필드를 노출하는 이 두 가지 방법을 downward API 라고 한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
이 연습에서는 컨테이너가 한 개 있는 파드를 생성하고, 해당 파드의 파드 수준(Pod-level) 필드를 실행 중인 컨테이너에 파일 형태로 생성한다. 다음은 파드를 위한 매니페스트를 보여준다.
apiVersion: v1
kind: Pod
metadata:
name: kubernetes-downwardapi-volume-example
labels:
zone: us-est-coast
cluster: test-cluster1
rack: rack-22
annotations:
build: two
builder: john-doe
spec:
containers:
- name: client-container
image: registry.k8s.io/busybox
command: ["sh", "-c"]
args:
- while true; do
if [[ -e /etc/podinfo/labels ]]; then
echo -en '\n\n'; cat /etc/podinfo/labels; fi;
if [[ -e /etc/podinfo/annotations ]]; then
echo -en '\n\n'; cat /etc/podinfo/annotations; fi;
sleep 5;
done;
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "annotations"
fieldRef:
fieldPath: metadata.annotations
매니페스트에서, 파드에 downwardAPI
볼륨이 있고,
컨테이너가 /etc/podinfo
에 볼륨을 마운트하는 것을 확인할 수 있다.
downwardAPI
아래의 items
배열을 살펴보자. 배열의 각 요소는
downwardAPI
볼륨을 의미한다.
첫 번째 요소는 파드의 metadata.labels
필드 값이
labels
라는 파일에 저장되어야 함을 지정한다.
두 번째 요소는 파드의 annotations
필드 값이
annotations
라는 파일에 저장되어야 함을 지정한다.
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/inject/dapi-volume.yaml
파드의 컨테이너가 실행 중인지 확인한다.
kubectl get pods
컨테이너의 로그를 본다.
kubectl logs kubernetes-downwardapi-volume-example
출력은 labels
파일과 annotations
파일의 내용을 보여준다.
cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"
build="two"
builder="john-doe"
파드에서 실행 중인 컨테이너의 셸을 가져온다.
kubectl exec -it kubernetes-downwardapi-volume-example -- sh
셸에서 labels
파일을 보자.
/# cat /etc/podinfo/labels
출력을 통해 모든 파드의 레이블이
labels
파일에 기록되었음을 확인할 수 있다.
cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"
마찬가지로 annotations
파일을 확인하자.
/# cat /etc/podinfo/annotations
etc/podinfo
디렉터리에 파일을 확인하자.
/# ls -laR /etc/podinfo
출력에서 labels
및 annotations
파일이
임시 하위 디렉터리에 있음을 알 수 있다. 이 예제에서는
..2982_06_02_21_47_53.299460680
이다. /etc/podinfo
디렉터리에서 ..data
는
임시 하위 디렉토리에 대한 심볼릭 링크이다. /etc/podinfo
디렉토리에서
labels
와 annotations
또한 심볼릭 링크이다.
drwxr-xr-x ... Feb 6 21:47 ..2982_06_02_21_47_53.299460680
lrwxrwxrwx ... Feb 6 21:47 ..data -> ..2982_06_02_21_47_53.299460680
lrwxrwxrwx ... Feb 6 21:47 annotations -> ..data/annotations
lrwxrwxrwx ... Feb 6 21:47 labels -> ..data/labels
/etc/..2982_06_02_21_47_53.299460680:
total 8
-rw-r--r-- ... Feb 6 21:47 annotations
-rw-r--r-- ... Feb 6 21:47 labels
심볼릭 링크를 사용하면 메타데이터의 동적(dynamic) 원자적(atomic) 갱신이 가능하다.
업데이트는 새 임시 디렉터리에 기록되고, ..data
심볼릭 링크는
rename(2)을 사용하여 원자적(atomic)으로 갱신한다.
셸을 종료한다.
/# exit
이전 연습에서는 downward API를 사용하여 파드 수준 필드에 액세스할 수 있도록 했다. 이번 연습에서는 파드 전체가 아닌 특정 컨테이너의 일부 필드만 전달한다. 다음은 기존과 마찬가지로 하나의 컨테이너만 가진 파드의 매니페스트를 보여준다.
apiVersion: v1
kind: Pod
metadata:
name: kubernetes-downwardapi-volume-example-2
spec:
containers:
- name: client-container
image: registry.k8s.io/busybox:1.24
command: ["sh", "-c"]
args:
- while true; do
echo -en '\n';
if [[ -e /etc/podinfo/cpu_limit ]]; then
echo -en '\n'; cat /etc/podinfo/cpu_limit; fi;
if [[ -e /etc/podinfo/cpu_request ]]; then
echo -en '\n'; cat /etc/podinfo/cpu_request; fi;
if [[ -e /etc/podinfo/mem_limit ]]; then
echo -en '\n'; cat /etc/podinfo/mem_limit; fi;
if [[ -e /etc/podinfo/mem_request ]]; then
echo -en '\n'; cat /etc/podinfo/mem_request; fi;
sleep 5;
done;
resources:
requests:
memory: "32Mi"
cpu: "125m"
limits:
memory: "64Mi"
cpu: "250m"
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: "cpu_limit"
resourceFieldRef:
containerName: client-container
resource: limits.cpu
divisor: 1m
- path: "cpu_request"
resourceFieldRef:
containerName: client-container
resource: requests.cpu
divisor: 1m
- path: "mem_limit"
resourceFieldRef:
containerName: client-container
resource: limits.memory
divisor: 1Mi
- path: "mem_request"
resourceFieldRef:
containerName: client-container
resource: requests.memory
divisor: 1Mi
매니페스트에서 파드에 downwardAPI
볼륨이 있고
단일 컨테이너는 /etc/podinfo
에
볼륨을 마운트하는 것을 확인할 수 있다.
downwardAPI
아래의 items
배열을 살펴보자.
배열의 각 요소는 downward API 볼륨의 파일을 의미한다.
첫 번째 요소는 client-container
라는 컨테이너에서
1m
으로 지정된 형식의 limits.cpu
필드 값이
cpu_limit
이라는 파일에 배포되어야 함을 지정한다.
divisor
필드는 선택 사항이며, 지정하지 않을 경우 1
의 값을 갖는다.
divisor 1은 cpu
리소스를 위한 코어 수를 의미하거나 memory
리소스에 대한 바이트 수를 의미한다.
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/inject/dapi-volume-resources.yaml
파드에서 실행 중인 컨테이너의 셸을 가져온다.
kubectl exec -it kubernetes-downwardapi-volume-example-2 -- sh
셸에서 cpu_limit
파일을 확인한다.
# 컨테이너 내부의 쉘에서 실행하자.
cat /etc/podinfo/cpu_limit
비슷한 명령을 통해 cpu_request
, mem_limit
및
mem_request
파일을 확인할 수 있다.
키(key)를 파드 안의 특정 경로에, 특정 권한으로, 파일 단위로 투영(project)할 수 있다. 자세한 내용은 시크릿(Secrets)을 참조한다.
spec
을 읽어보자.
파드에 대한 API 정의다. 여기에는 컨테이너 (파드의 일부)의 정의가 포함되어 있다.레거시 API 레퍼런스에서 볼륨에 대해 읽어본다.
Volume
API 정의를 확인한다.DownwardAPIVolumeSource
API 정의를 확인한다.DownwardAPIVolumeFile
API 정의를 확인한다.ResourceFieldSelector
API 정의를 확인한다.본 페이지는 암호 및 암호화 키와 같은 민감한 데이터를 파드에 안전하게 주입하는 방법을 설명한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
사용자 이름 my-app
과 비밀번호 39528$vdg7Jb
의 두 가지 시크릿 데이터가 필요하다고 가정한다.
먼저 base64 인코딩 도구를 사용하여 사용자 이름과 암호를 base64 표현으로 변환한다. 다음은 일반적으로 사용 가능한 base64 프로그램을 사용하는 예제이다.
echo -n 'my-app' | base64
echo -n '39528$vdg7Jb' | base64
사용자 이름의 base-64 표현이 bXktYXBw
이고 암호의 base-64 표현이 Mzk1MjgkdmRnN0pi
임을
출력을 통해 확인할 수 있다.
다음은 사용자 이름과 암호가 들어 있는 시크릿을 생성하는 데 사용할 수 있는 구성 파일이다.
apiVersion: v1
kind: Secret
metadata:
name: test-secret
data:
username: bXktYXBw
password: Mzk1MjgkdmRnN0pi
시크릿을 생성한다.
kubectl apply -f https://k8s.io/examples/pods/inject/secret.yaml
시크릿에 대한 정보를 확인한다.
kubectl get secret test-secret
결과는 다음과 같다.
NAME TYPE DATA AGE
test-secret Opaque 2 1m
시크릿에 대한 자세한 정보를 확인한다.
kubectl describe secret test-secret
결과는 다음과 같다.
Name: test-secret
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 13 bytes
username: 7 bytes
Base64 인코딩 단계를 건너뛰려면 kubectl create secret
명령을 사용하여
동일한 Secret을 생성할 수 있다. 다음은 예시이다.
kubectl create secret generic test-secret --from-literal='username=my-app' --from-literal='password=39528$vdg7Jb'
이와 같이 더 편리하게 사용할 수 있다. 앞에서 설명한 자세한 접근 방식은 각 단계를 명시적으로 실행하여 현재 상황을 확인할 수 있다.
다음은 파드를 생성하는 데 사용할 수 있는 구성 파일이다.
apiVersion: v1
kind: Pod
metadata:
name: secret-test-pod
spec:
containers:
- name: test-container
image: nginx
volumeMounts:
# name must match the volume name below
- name: secret-volume
mountPath: /etc/secret-volume
# The secret data is exposed to Containers in the Pod through a Volume.
volumes:
- name: secret-volume
secret:
secretName: test-secret
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/inject/secret-pod.yaml
파드가 실행중인지 확인한다.
kubectl get pod secret-test-pod
Output:
NAME READY STATUS RESTARTS AGE
secret-test-pod 1/1 Running 0 42m
파드에서 실행 중인 컨테이너의 셸을 가져오자.
kubectl exec -i -t secret-test-pod -- /bin/bash
시크릿 데이터는 /etc/secret-volume
에 마운트된 볼륨을 통해
컨테이너에 노출된다.
셸에서 /etc/secret-volume
디렉터리의 파일을 나열한다.
# 컨테이너 내부의 셸에서 실행하자
ls /etc/secret-volume
두 개의 파일과 각 파일의 시크릿 데이터 조각을 확인할 수 있다.
password username
셸에서 username
및 password
파일의 내용을 출력한다.
# 컨테이너 내부의 셸에서 실행하자
echo "$( cat /etc/secret-volume/username )"
echo "$( cat /etc/secret-volume/password )"
사용자 이름과 비밀번호가 출력된다.
my-app
39528$vdg7Jb
환경 변수를 시크릿의 키-값 쌍으로 정의한다.
kubectl create secret generic backend-user --from-literal=backend-username='backend-admin'
시크릿에 정의된 backend-username
값을 파드 명세의 SECRET_USERNAME
환경 변수에 할당한다.
apiVersion: v1
kind: Pod
metadata:
name: env-single-secret
spec:
containers:
- name: envars-test-container
image: nginx
env:
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: backend-user
key: backend-username
파드를 생성한다.
kubectl create -f https://k8s.io/examples/pods/inject/pod-single-secret-env-variable.yaml
셸에서 SECRET_USERNAME
컨테이너 환경 변수의 내용을 출력한다.
kubectl exec -i -t env-single-secret -- /bin/sh -c 'echo $SECRET_USERNAME'
출력은 다음과 같다.
backend-admin
이전 예제와 마찬가지로 시크릿을 먼저 생성한다.
kubectl create secret generic backend-user --from-literal=backend-username='backend-admin'
kubectl create secret generic db-user --from-literal=db-username='db-admin'
파드 명세에 환경 변수를 정의한다.
apiVersion: v1
kind: Pod
metadata:
name: envvars-multiple-secrets
spec:
containers:
- name: envars-test-container
image: nginx
env:
- name: BACKEND_USERNAME
valueFrom:
secretKeyRef:
name: backend-user
key: backend-username
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-user
key: db-username
파드를 생성한다.
kubectl create -f https://k8s.io/examples/pods/inject/pod-multiple-secret-env-variable.yaml
셸에서 컨테이너 환경 변수를 출력한다.
kubectl exec -i -t envvars-multiple-secrets -- /bin/sh -c 'env | grep _USERNAME'
출력은 다음과 같다.
DB_USERNAME=db-admin
BACKEND_USERNAME=backend-admin
여러 키-값 쌍을 포함하는 시크릿을 생성한다.
kubectl create secret generic test-secret --from-literal=username='my-app' --from-literal=password='39528$vdg7Jb'
envFrom을 사용하여 시크릿의 모든 데이터를 컨테이너 환경 변수로 정의한다. 시크릿의 키는 파드에서 환경 변수의 이름이 된다.
apiVersion: v1
kind: Pod
metadata:
name: envfrom-secret
spec:
containers:
- name: envars-test-container
image: nginx
envFrom:
- secretRef:
name: test-secret
파드를 생성한다.
kubectl create -f https://k8s.io/examples/pods/inject/pod-secret-envFrom.yaml
username
및 password
컨테이너 환경 변수를 셸에서 출력한다.
kubectl exec -i -t envfrom-secret -- /bin/sh -c 'echo "username: $username\npassword: $password\n"'
출력은 다음과 같다.
username: my-app
password: 39528$vdg7Jb
이 페이지에서는 쿠버네티스 디플로이먼트 오브젝트를 사용하여 애플리케이션을 실행하는 방법을 설명한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.9. 버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
쿠버네티스 디플로이먼트 오브젝트를 생성하여 애플리케이션을 실행할 수 있으며, 디플로이먼트에 대한 명세를 YAML 파일에 기술할 수 있다. 예를 들어 이 YAML 파일은 nginx:1.14.2 도커 이미지를 실행하는 디플로이먼트에 대한 명세를 담고 있다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
YAML 파일을 기반으로 디플로이먼트를 생성한다.
kubectl apply -f https://k8s.io/examples/application/deployment.yaml
디플로이먼트에 대한 정보를 살펴본다.
kubectl describe deployment nginx-deployment
출력은 다음과 유사하다.
Name: nginx-deployment
Namespace: default
CreationTimestamp: Tue, 30 Aug 2016 18:11:37 -0700
Labels: app=nginx
Annotations: deployment.kubernetes.io/revision=1
Selector: app=nginx
Replicas: 2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.14.2
Port: 80/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-1771418926 (2/2 replicas created)
No events.
디플로이먼트에 의해 생성된 파드를 나열한다.
kubectl get pods -l app=nginx
출력은 다음과 유사하다.
NAME READY STATUS RESTARTS AGE
nginx-deployment-1771418926-7o5ns 1/1 Running 0 16h
nginx-deployment-1771418926-r18az 1/1 Running 0 16h
파드에 대한 정보를 살펴본다.
kubectl describe pod <pod-name>
<pod-name>
은 파드 중 하나의 이름이다.
새 YAML 파일을 적용하여 디플로이먼트를 업데이트할 수 있다. 이 YAML 파일은 nginx 1.16.1을 사용하도록 디플로이먼트를 업데이트해야 함을 명시하고 있다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.16.1 # Update the version of nginx from 1.14.2 to 1.16.1
ports:
- containerPort: 80
새 YAML 파일을 적용한다.
kubectl apply -f https://k8s.io/examples/application/deployment-update.yaml
디플로이먼트가 새 이름으로 파드를 생성하고 이전 파드를 삭제하는 것을 확인한다.
kubectl get pods -l app=nginx
새 YAML 파일을 적용하여 디플로이먼트의 파드 수를 늘릴 수 있다.
이 YAML 파일은 replicas
를 4로 설정하여 디플로이먼트에
4개의 파드가 있어야 함을 명시하고 있다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 4 # Update the replicas from 2 to 4
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.16.1
ports:
- containerPort: 80
새 YAML 파일을 적용한다.
kubectl apply -f https://k8s.io/examples/application/deployment-scale.yaml
디플로이먼트에 4개의 파드가 있는지 확인한다.
kubectl get pods -l app=nginx
출력은 다음과 유사하다.
NAME READY STATUS RESTARTS AGE
nginx-deployment-148880595-4zdqq 1/1 Running 0 25s
nginx-deployment-148880595-6zgi1 1/1 Running 0 25s
nginx-deployment-148880595-fxcez 1/1 Running 0 2m
nginx-deployment-148880595-rwovn 1/1 Running 0 2m
이름으로 디플로이먼트를 삭제한다.
kubectl delete deployment nginx-deployment
애플리케이션을 복제하여 생성하는 기본적인 방법은 내부적으로 레플리카셋(ReplicaSet)을 활용하는 디플로이먼트를 사용하는 것이다. 쿠버네티스에 디플로이먼트 및 레플리카셋이 도입되기 전에는 레플리케이션컨트롤러(ReplicationController)를 사용하여 복제 애플리케이션을 구성했었다.
이 페이지에서는 쿠버네티스 클러스터에서 퍼시스턴트볼륨(PersistentVolume)과 디플로이먼트(Deployment)를 사용하여, 단일 인스턴스 스테이트풀 애플리케이션을 실행하는 방법을 보인다. 해당 애플리케이션은 MySQL이다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
여기에서 사용된 퍼시스턴트볼륨클레임을 만족하기 위하여, 기본 스토리지클래스가 설정되어 있는 상태의 동적 퍼시스턴트볼륨 프로비저너, 또는 정적으로(statically) 프로비저닝된 퍼시스턴트볼륨이 필요하다.
쿠버네티스 디플로이먼트를 생성하고 퍼시스턴트볼륨클레임(PersistentVolumeClaim)을 사용하는 기존 퍼시스턴트볼륨에 연결하여 스테이트풀 애플리케이션을 실행할 수 있다. 예를 들어, 다음 YAML 파일은 MySQL을 실행하고 퍼시스턴트볼륨클레임을 참조하는 디플로이먼트를 기술한다. 이 파일은 /var/lib/mysql에 대한 볼륨 마운트를 정의한 후에, 20G의 볼륨을 요청하는 퍼시트턴트볼륨클레임을 생성한다. 이 클레임은 요구 사항에 적합한 기존 볼륨이나 동적 프로비저너에 의해서 충족된다.
참고: config yaml 파일에 정의된 비밀번호는 안전하지 않다. 더 안전한 해결방법을 위해 쿠버네티스 시크릿 을 보자
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
ports:
- port: 3306
selector:
app: mysql
clusterIP: None
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
# Use secret in real usage
- name: MYSQL_ROOT_PASSWORD
value: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
YAML 파일의 PV와 PVC를 배포한다.
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-pv.yaml
YAML 파일의 다른 오브젝트들을 배포한다.
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-deployment.yaml
디플로이먼트에 관한 정보를 확인한다.
kubectl describe deployment mysql
출력은 다음과 유사하다.
Name: mysql
Namespace: default
CreationTimestamp: Tue, 01 Nov 2016 11:18:45 -0700
Labels: app=mysql
Annotations: deployment.kubernetes.io/revision=1
Selector: app=mysql
Replicas: 1 desired | 1 updated | 1 total | 0 available | 1 unavailable
StrategyType: Recreate
MinReadySeconds: 0
Pod Template:
Labels: app=mysql
Containers:
mysql:
Image: mysql:5.6
Port: 3306/TCP
Environment:
MYSQL_ROOT_PASSWORD: password
Mounts:
/var/lib/mysql from mysql-persistent-storage (rw)
Volumes:
mysql-persistent-storage:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: mysql-pv-claim
ReadOnly: false
Conditions:
Type Status Reason
---- ------ ------
Available False MinimumReplicasUnavailable
Progressing True ReplicaSetUpdated
OldReplicaSets: <none>
NewReplicaSet: mysql-63082529 (1/1 replicas created)
Events:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
33s 33s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set mysql-63082529 to 1
디플로이먼트로 생성된 파드를 나열한다.
kubectl get pods -l app=mysql
출력은 다음과 유사하다.
NAME READY STATUS RESTARTS AGE
mysql-63082529-2z3ki 1/1 Running 0 3m
퍼시스턴트볼륨클레임을 살펴본다.
kubectl describe pvc mysql-pv-claim
출력은 다음과 유사하다.
Name: mysql-pv-claim
Namespace: default
StorageClass:
Status: Bound
Volume: mysql-pv-volume
Labels: <none>
Annotations: pv.kubernetes.io/bind-completed=yes
pv.kubernetes.io/bound-by-controller=yes
Capacity: 20Gi
Access Modes: RWO
Events: <none>
이전의 YAML 파일은 클러스터의 다른 파드가 데이터베이스에
접근할 수 있는 서비스를 생성한다. clusterIP: None
서비스 옵션을 사용하면 서비스의 DNS 이름을 직접 파드의 IP 주소로
해석하도록 처리한다. 이 방법은 서비스에서 연결되는 파드가 오직 하나 뿐이고,
파드의 수를 더 늘릴 필요가 없는 경우에 가장 적합하다.
서버에 접속하기 위하여 MySQL 클라이언트를 실행한다.
kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword
이 명령어는 MySQL 클라이언트를 실행하는 파드를 클러스터에 생성하고, 서비스를 통하여 서버에 연결한다. 연결된다면, 스테이트풀 MySQL 데이터베이스가 실행 중임을 알 수 있다.
Waiting for pod default/mysql-client-274442439-zyp6i to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.
mysql>
kubectl apply
명령을 사용하여 기존과 동일한 방식으로 디플로이먼트의
이미지나 다른 부분을 변경할 수 있다. 스테이트풀 애플리케이션과 관련하여 몇 가지
주의 사항이 있다.
strategy:
type: Recreate
를 사용한다. 이는 쿠버네티스가
롤링 업데이트를 사용하지 않도록 지시한다. 동시에 두 개 이상의 파드를 생성할
수 없으므로, 롤링 업데이트는 일어나지 않게 된다. Recreate
전략을 사용하면
변경된 구성으로 새로운 파드를 생성하기에 앞서 기존의 파드를 중단한다.이름으로 배포된 오브젝트를 삭제한다.
kubectl delete deployment,svc mysql
kubectl delete pvc mysql-pv-claim
kubectl delete pv mysql-pv-volume
퍼시스턴트볼륨을 수동으로 프로비저닝한 경우라면, 동일하게 수동으로 삭제하고 기본 리소스도 해제해야 한다. 동적 프로비저너를 사용한 경우, 퍼시스턴트볼륨클레임이 삭제되었을 때에 퍼시스턴트볼륨 또한 자동으로 삭제된다. 일부 동적 프로비저너(EBS 와 PD와 같은)는 퍼시스턴트볼륨을 삭제할 때에 기본 리소스도 해제한다.
디플로이먼트 오브젝트에 대해 더 배워 보기
애플리케이션 배포하기에 대해 더 배워보기
이 페이지에서는 스테이트풀셋(StatefulSet) 으로 복제 스테이트풀 애플리케이션을 실행하는 방법에 대해 소개한다. 이 애플리케이션은 복제 MySQL 데이터베이스이다. 이 예제의 토폴로지는 단일 주 서버와 여러 복제 서버로 이루어져있으며, row-based 비동기 복제 방식을 사용한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
여기에서 사용된 퍼시스턴트볼륨클레임을 만족하기 위하여, 기본 스토리지클래스가 설정되어 있는 상태의 동적 퍼시스턴트볼륨 프로비저너, 또는 정적으로(statically) 프로비저닝된 퍼시스턴트볼륨이 필요하다.
MySQL 디플로이먼트 예시는 컨피그맵과, 2개의 서비스, 그리고 스테이트풀셋으로 구성되어 있다.
다음 YAML 설정 파일로부터 컨피그맵을 생성한다.
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql
labels:
app: mysql
app.kubernetes.io/name: mysql
data:
primary.cnf: |
# Primary에만 이 구성을 적용한다.
[mysqld]
log-bin
replica.cnf: |
# 레플리카에만 이 구성을 적용한다.
[mysqld]
super-read-only
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-configmap.yaml
이 컨피그맵은 당신이 독립적으로 주 MySQL 서버와 레플리카들의 설정을 컨트롤할 수 있도록
my.cnf
을 오버라이드한다.
이 경우에는, 주 서버는 복제 로그를 레플리카들에게 제공하고
레플리카들은 복제를 통한 쓰기가 아닌 다른 쓰기들은 거부하도록 할 것이다.
컨피그맵 자체가 다른 파드들에 서로 다른 설정 영역이 적용되도록 하는 것이 아니다. 스테이트풀셋 컨트롤러가 제공해주는 정보에 따라서, 각 파드들은 초기화되면서 설정 영역을 참조할지 결정한다.
다음 YAML 설정 파일로부터 서비스를 생성한다.
# 스테이트풀셋 멤버의 안정적인 DNS 엔트리를 위한 헤드리스 서비스.
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
app.kubernetes.io/name: mysql
spec:
ports:
- name: mysql
port: 3306
clusterIP: None
selector:
app: mysql
---
# 읽기용 MySQL 인스턴스에 연결하기 위한 클라이언트 서비스.
# 쓰기용은 Primary인 mysql-0.mysql에 대신 연결해야 한다.
apiVersion: v1
kind: Service
metadata:
name: mysql-read
labels:
app: mysql
app.kubernetes.io/name: mysql
readonly: "true"
spec:
ports:
- name: mysql
port: 3306
selector:
app: mysql
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-services.yaml
헤드리스 서비스는 스테이트풀셋
컨트롤러가 집합의 일부분인
파드들을 위해 생성한 DNS 엔트리들(entries)의 위한 거점이 된다.
헤드리스 서비스의 이름이 mysql
이므로, 파드들은
같은 쿠버네티스 클러스터나 네임스페이스에 존재하는 다른 파드들에게 <pod-name>.mysql
라는 이름으로
접근될 수 있다.
mysql-read
라고 불리우는 클라이언트 서비스는 고유의 클러스터 IP를 가지며,
Ready 상태인 모든 MySQL 파드들에게 커넥션을 분배하는 일반적인 서비스이다.
잠재적인 엔드포인트들의 집합은 주 MySQL 서버와 해당
레플리카들을 포함한다.
오직 읽기 쿼리들만 로드-밸런싱된 클라이언트 서비스를 이용할 수 있다는 사실에 주목하자. 하나의 주 MySQL 서버만이 존재하기 떄문에, 클라이언트들은 쓰기 작업을 실행하기 위해서 주 MySQL 파드에 (헤드리스 서비스 안에 존재하는 DNS 엔트리를 통해) 직접 접근해야 한다.
마지막으로, 다음 YAML 설정 파일로부터 스테이트풀셋을 생성한다.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
app.kubernetes.io/name: mysql
serviceName: mysql
replicas: 3
template:
metadata:
labels:
app: mysql
app.kubernetes.io/name: mysql
spec:
initContainers:
- name: init-mysql
image: mysql:5.7
command:
- bash
- "-c"
- |
set -ex
# 파드의 원래 인덱스에서 mysql server-id를 생성.
[[ $HOSTNAME =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
echo [mysqld] > /mnt/conf.d/server-id.cnf
# 예약된 server-id=0 값을 피하기 위해 오프셋 추가.
echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
# config-map에서 emptyDir로 적당한 conf.d 파일들을 복사.
if [[ $ordinal -eq 0 ]]; then
cp /mnt/config-map/primary.cnf /mnt/conf.d/
else
cp /mnt/config-map/replica.cnf /mnt/conf.d/
fi
volumeMounts:
- name: conf
mountPath: /mnt/conf.d
- name: config-map
mountPath: /mnt/config-map
- name: clone-mysql
image: gcr.io/google-samples/xtrabackup:1.0
command:
- bash
- "-c"
- |
set -ex
# 데이터가 이미 존재하면 복제 생략.
[[ -d /var/lib/mysql/mysql ]] && exit 0
# Primary에 복제 생략(ordinal index 0).
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
[[ $ordinal -eq 0 ]] && exit 0
# 이전 피어(peer)에서 데이터 복제.
ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
# 백업 준비.
xtrabackup --prepare --target-dir=/var/lib/mysql
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ALLOW_EMPTY_PASSWORD
value: "1"
ports:
- name: mysql
containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
resources:
requests:
cpu: 500m
memory: 1Gi
livenessProbe:
exec:
command: ["mysqladmin", "ping"]
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
exec:
# TCP 상에서 쿼리를 실행할 수 있는지 확인(skip-networking은 off).
command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
initialDelaySeconds: 5
periodSeconds: 2
timeoutSeconds: 1
- name: xtrabackup
image: gcr.io/google-samples/xtrabackup:1.0
ports:
- name: xtrabackup
containerPort: 3307
command:
- bash
- "-c"
- |
set -ex
cd /var/lib/mysql
# 복제된 데이터의 binlog 위치를 확인.
if [[ -f xtrabackup_slave_info && "x$(<xtrabackup_slave_info)" != "x" ]]; then
# XtraBackup은 기존 레플리카에서 복제하기 때문에
# 일부 "CHANGE MASTER TO" 쿼리는 이미 생성했음. (테일링 세미콜론을 제거해야 한다!)
cat xtrabackup_slave_info | sed -E 's/;$//g' > change_master_to.sql.in
# 이 경우에는 xtrabackup_binlog_info는 무시(필요없음).
rm -f xtrabackup_slave_info xtrabackup_binlog_info
elif [[ -f xtrabackup_binlog_info ]]; then
# Primary로부터 직접 복제함. binlog 위치를 파싱.
[[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
rm -f xtrabackup_binlog_info xtrabackup_slave_info
echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
fi
# Replication을 시작하여 복제를 완료해야 하는지 확인.
if [[ -f change_master_to.sql.in ]]; then
echo "Waiting for mysqld to be ready (accepting connections)"
until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done
echo "Initializing replication from clone position"
mysql -h 127.0.0.1 \
-e "$(<change_master_to.sql.in), \
MASTER_HOST='mysql-0.mysql', \
MASTER_USER='root', \
MASTER_PASSWORD='', \
MASTER_CONNECT_RETRY=10; \
START SLAVE;" || exit 1
# 컨테이너가 다시 시작하는 경우, 이 작업을 한번만 시도한다.
mv change_master_to.sql.in change_master_to.sql.orig
fi
# 피어가 요청할 때 서버를 시작하여 백업을 보냄.
exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
"xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root"
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
resources:
requests:
cpu: 100m
memory: 100Mi
volumes:
- name: conf
emptyDir: {}
- name: config-map
configMap:
name: mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-statefulset.yaml
다음을 실행하여, 초기화되는 프로세스들을 확인할 수 있다.
kubectl get pods -l app=mysql --watch
잠시 뒤에, 3개의 파드들이 Running
상태가 되는 것을 볼 수 있다.
NAME READY STATUS RESTARTS AGE
mysql-0 2/2 Running 0 2m
mysql-1 2/2 Running 0 1m
mysql-2 2/2 Running 0 1m
Ctrl+C를 입력하여 watch를 종료하자.
해당 메니페스트에는 스테이트풀셋의 일부분인 스테이트풀 파드들을 관리하기 위한 다양한 기법들이 적용되어 있다. 다음 섹션에서는 스테트풀셋이 파드들을 생성할 때 일어나는 일들을 이해할 수 있도록 일부 기법들을 강조하여 설명한다.
스테이트풀셋 컨트롤러는 파드들의 인덱스에 따라 순차적으로 시작시킨다. 컨트롤러는 다음 파드 생성 이전에 각 파드가 Ready 상태가 되었다고 알려줄 때까지 기다린다.
추가적으로, 컨트롤러는 각 파드들에게 <스테이트풀셋 이름>-<순차적 인덱스>
형태의
고유하고 안정적인 이름을 부여하는데, 결과적으로 파드들은 mysql-0
, mysql-1
,
그리고 mysql-2
라는 이름을 가지게 된다.
스테이트풀셋 매니페스트의 파드 템플릿은 해당 속성들을 통해 순차적인 MySQL 복제의 시작을 수행한다.
파드 스펙의 컨테이너를 시작하기 전에, 파드는 순서가 정의되어 있는 초기화 컨테이너들을 먼저 실행시킨다.
init-mysql
라는 이름의 첫 번째 초기화 컨테이너는, 인덱스에 따라
특별한 MySQL 설정 파일을 생성한다.
스크립트는 인덱스를 hostname
명령으로 반환되는 파드 이름의
마지막 부분에서 추출하여 결정한다.
그리고 인덱스(이미 사용된 값들을 피하기 위한 오프셋 숫자와 함께)를
MySQL의 conf.d
디렉토리의 server-id.cnf
파일에 저장한다.
이는 스테이트풀셋에게서 제공된 고유하고, 안정적인 신원을 같은 속성을
필요로 하는 MySQL 서버 ID의 형태로 바꾸어준다.
또한 init-mysql
컨테이너의 스크립트는 컨피그맵을 conf.d
로 복사하여,
primary.cnf
또는 replica.cnf
을 적용한다.
이 예제의 토폴로지가 하나의 주 MySQL 서버와 일정 수의 레플리카들로 이루어져 있기 때문에,
스크립트는 0
인덱스를 주 서버로, 그리고 나머지 값들은
레플리카로 지정한다.
스테이트풀셋 컨트롤러의
디플로이먼트와 스케일링 보증과
합쳐지면, 복제를 위한 레플리카들을 생성하기 전에 주 MySQL 서버가 Ready 상태가 되도록
보장할 수 있다.
일반적으로, 레플리카에 새로운 파드가 추가되는 경우, 주 MySQL 서버가 이미 데이터를 가지고 있다고 가정해야 한다. 또한 복제 로그가 첫 시작점부터의 로그들을 다 가지고 있지는 않을 수 있다고 가정해야 한다. 이러한 보수적인 가정들은 스테이트풀셋이 초기 크기로 고정되어 있는 것보다, 시간에 따라 확장/축소하게 할 수 있도록 하는 중요한 열쇠가 된다.
clone-mysql
라는 이름의 두 번째 초기화 컨테이너는, 퍼시스턴트볼륨에서 처음 초기화된
레플리카 파드에 복제 작업을 수행한다.
이 말은 다른 실행 중인 파드로부터 모든 데이터들을 복제하기 때문에,
로컬의 상태가 충분히 일관성을 유지하고 있어서 주 서버에서부터 복제를 시작할 수 있다는 의미이다.
MySQL 자체가 이러한 메커니즘을 제공해주지는 않기 때문에, 이 예제에서는 XtraBackup이라는
유명한 오픈소스 도구를 사용한다.
복제 중에는, 복제 대상 MySQL 서버가 성능 저하를 겪을 수 있다.
주 MySQL 서버의 이러한 충격을 최소화하기 위해, 스크립트는 각 파드가 자신의 인덱스보다
하나 작은 파드로부터 복제하도록 지시한다.
이것이 정상적으로 동작하는 이유는 스테이트풀셋 컨트롤러가 파드 N+1
을 실행하기 전에
항상 파드 N
이 Ready 상태라는 것을 보장하기 때문이다.
초기화 컨테이너들의 성공적으로 완료되면, 일반적인 컨테이너가 실행된다.
MySQL 파드는 mysqld
서버를 구동하는 mysql
컨테이너로 구성되어 있으며,
xtrabackup
컨테이너는
사이드카(sidecar)로서 작동한다.
xtrabackup
사이드카는 복제된 데이터 파일들을 보고 레플리카에 MySQL 복제를 시작해야 할
필요가 있는지 결정한다.
만약 그렇다면, mysqld
이 준비될 때까지 기다린 후 CHANGE MASTER TO
,
그리고 START SLAVE
를 XtraBackup 복제(clone) 파일들에서
추출한 복제(replication) 파라미터들과 함께 실행시킨다.
레플리카가 복제를 시작하면, 먼저 주 MySQL 서버를 기억하고, 서버가 재시작되거나
커넥션이 끊어지면 다시 연결한다.
또한 레플리카들은 주 서버를 안정된 DNS 이름
(mysql-0.mysql
)으로 찾기 때문에, 주 서버가 리스케쥴링에 의해 새로운
파드 IP를 받아도 주 서버를 자동으로 찾는다.
마지막으로, 복제를 시작한 후에는, xtrabackup
컨테이너는 데이터 복제를 요청하는
다른 파드들의 커넥션을 리스닝한다.
이 서버는 스테이트풀셋이 확장하거나, 다음 파드가 퍼시스턴트볼륨클레임을 잃어서 다시 복제를
수행해햐 할 경우를 대비하여 독립적으로 존재해야 한다.
임시 컨테이너를 mysql:5.7
이미지로 실행하고 mysql
클라이언트
바이너리를 실행하는 것으로 테스트 쿼리를 주 MySQL 서버(mysql-0.mysql
호스트네임)로
보낼 수 있다.
kubectl run mysql-client --image=mysql:5.7 -i --rm --restart=Never --\
mysql -h mysql-0.mysql <<EOF
CREATE DATABASE test;
CREATE TABLE test.messages (message VARCHAR(250));
INSERT INTO test.messages VALUES ('hello');
EOF
mysql-read
호스트네임을 사용하여 Ready 상태가 되었다고 보고하는 어느 서버에나 시험 쿼리를
보낼 수 있다.
kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
mysql -h mysql-read -e "SELECT * FROM test.messages"
그러면 다음과 같은 출력를 얻을 것이다.
Waiting for pod default/mysql-client to be running, status is Pending, pod ready: false
+---------+
| message |
+---------+
| hello |
+---------+
pod "mysql-client" deleted
mysql-read
서비스가 커넥션을 서버들에게 분배하고 있다는 사실을 확인하기 위해서,
SELECT @@server_id
를 루프로 돌릴 수 있다.
kubectl run mysql-client-loop --image=mysql:5.7 -i -t --rm --restart=Never --\
bash -ic "while sleep 1; do mysql -h mysql-read -e 'SELECT @@server_id,NOW()'; done"
보고된 @@server_id
가 무작위로 바뀌는 것을 알 수 있다. 왜냐하면 매 커넥션 시도마다
다른 엔드포인트가 선택될 수 있기 때문이다.
+-------------+---------------------+
| @@server_id | NOW() |
+-------------+---------------------+
| 100 | 2006-01-02 15:04:05 |
+-------------+---------------------+
+-------------+---------------------+
| @@server_id | NOW() |
+-------------+---------------------+
| 102 | 2006-01-02 15:04:06 |
+-------------+---------------------+
+-------------+---------------------+
| @@server_id | NOW() |
+-------------+---------------------+
| 101 | 2006-01-02 15:04:07 |
+-------------+---------------------+
루프를 끝내고 싶다면 Ctrl+C를 입력하면 되지만, 다음 단계에서의 영향을 보기 위해서 다른 창에 계속 실행시켜 놓는 것이 좋다.
단일 서버 대비 레플리카들의 풀에 의한 읽기의 가용성 향상을
시연하기 위해, 파드를 Ready 상태로 강제하면서 SELECT @@server_id
루프를 돌리자.
mysql
컨테이너의 준비성 프로브는
쿼리를 실행할 수 있는지를 확인하기 위해 mysql -h 127.0.0.1 -e 'SELECT 1'
명령을 실행한다.
준비성 프로브를 강제로 실패시키는 방법은 해당 명령을 고장내는 것이다.
kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql /usr/bin/mysql.off
이것은 mysql-2
파드의 실제 컨테이너의 파일시스템에 접근해서 mysql
명령의 이름을 변경하여,
준비성 프로브가 찾을 수 없도록 한다.
몇 초 뒤에, 파드는 컨터이너 중 하나가 준비되지 않았다고 보고할 것이다.
다음 명령을 실행시켜서 확인할 수 있다.
kubectl get pod mysql-2
READY
항목의 1/2
를 확인하자.
NAME READY STATUS RESTARTS AGE
mysql-2 1/2 Running 0 3m
이 시점에서, 102
를 보고하지 않지만, 계속해서 SELECT @@server_id
루프가 실행되는 것을
확인할 수 있을 것이다.
init-mysql
스크립트에서 server-id
를 100 + $ordinal
로 정의했기 때문에,
서버 ID 102
는 mysql-2
파드에 대응된다는 사실을 상기하자.
이제 파드를 고치면 몇 초 뒤에 루프 출력에 나타날 것이다.
kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql.off /usr/bin/mysql
레플리카셋이 스테이트리스 파드들에게 하는 것처럼, 스테이트풀셋 또한 파드들이 삭제되면 파드들을 다시 생성한다.
kubectl delete pod mysql-2
스테이트풀셋 컨트롤러는 mysql-2
파드가 존재하지 않는 것을 인지하고,
같은 이름으로 새로운 파드를 생성하고 같은
퍼시스턴트볼륨클레임에 연결한다.
잠시동안 서버 ID 102
가 루프 출력에서 사라지고 다시 나타나는 것을
확인할 수 있을 것이다.
만약 당신의 쿠버네티스 클러스터가 여러 노드들을 가지고 있으면, 드레인을 사용하여 노드 다운타임을 시뮬레이션할 수 있다.
먼저 MySQL 파드가 존재하고 있는 노드를 확인하자.
kubectl get pod mysql-2 -o wide
노드 이름은 마지막 열에서 나타날 것이다.
NAME READY STATUS RESTARTS AGE IP NODE
mysql-2 2/2 Running 0 15m 10.244.5.27 kubernetes-node-9l2t
그 후에, 다음 명령을 실행하여 노드를 드레인한다.
그러면 새로운 파드가 스케줄되지 않게 방지하고(cordon), 기존의 파드들을 추방(evict)한다.
<node-name>
를 이전 단계에서 찾았던 노드의 이름으로 바꾸자.
# 위에 명시된 다른 워크로드들이 받는 영향에 대한 주의사항을 확인한다.
kubectl drain <node-name> --force --delete-emptydir-data --ignore-daemonsets
이제 파드가 다른 노드에 리스케줄링되는 것을 관찰할 수 있다.
kubectl get pod mysql-2 -o wide --watch
출력은 다음과 비슷할 것이다.
NAME READY STATUS RESTARTS AGE IP NODE
mysql-2 2/2 Terminating 0 15m 10.244.1.56 kubernetes-node-9l2t
[...]
mysql-2 0/2 Pending 0 0s <none> kubernetes-node-fjlm
mysql-2 0/2 Init:0/2 0 0s <none> kubernetes-node-fjlm
mysql-2 0/2 Init:1/2 0 20s 10.244.5.32 kubernetes-node-fjlm
mysql-2 0/2 PodInitializing 0 21s 10.244.5.32 kubernetes-node-fjlm
mysql-2 1/2 Running 0 22s 10.244.5.32 kubernetes-node-fjlm
mysql-2 2/2 Running 0 30s 10.244.5.32 kubernetes-node-fjlm
그리고, 서버 ID 102
가 SELECT @@server_id
루프 출력에서 잠시
사라진 후 다시 보이는 것을 확인할 수 있을 것이다.
이제 노드의 스케줄 방지를 다시 해제(uncordon)해서 정상으로 돌아가도록 조치한다.
kubectl uncordon <node-name>
MySQL 레플리케이션을 사용하면, 레플리카를 추가하는 것으로 읽기 쿼리 용량을 키울 수 있다. 스테이트풀셋을 사용하면, 단 한 줄의 명령으로 달성할 수 있다.
kubectl scale statefulset mysql --replicas=5
명령을 실행시켜서 새로운 파드들이 올라오는 것을 관찰하자.
kubectl get pods -l app=mysql --watch
파드들이 올라오면, SELECT @@server_id
루프 출력에 서버 ID 103
과 104
가 나타나기
시작할 것이다.
그리고 해당 파드들이 존재하기 전에 추가된 데이터들이 해당 새 서버들에게도 존재하는 것을 확인할 수 있다.
kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
mysql -h mysql-3.mysql -e "SELECT * FROM test.messages"
Waiting for pod default/mysql-client to be running, status is Pending, pod ready: false
+---------+
| message |
+---------+
| hello |
+---------+
pod "mysql-client" deleted
축소하는 것도 간단하게 할 수 있다.
kubectl scale statefulset mysql --replicas=3
확장은 퍼시스턴트볼륨클레임을 자동으로 생성하지만, 축소에서는 해당 PVC들이 자동으로 삭제되지 않는다.
이로써 확장을 빠르게 하기 위해 초기화된 PVC들을 보관해 두거나, 삭제하기 전에 데이터를 추출하는 선택을 할 수 있다.
다음 명령을 실행하여 확인할 수 있다.
kubectl get pvc -l app=mysql
스테이트풀셋을 3으로 축소했음에도 PVC 5개가 아직 남아있음을 보여준다.
NAME STATUS VOLUME CAPACITY ACCESSMODES AGE
data-mysql-0 Bound pvc-8acbf5dc-b103-11e6-93fa-42010a800002 10Gi RWO 20m
data-mysql-1 Bound pvc-8ad39820-b103-11e6-93fa-42010a800002 10Gi RWO 20m
data-mysql-2 Bound pvc-8ad69a6d-b103-11e6-93fa-42010a800002 10Gi RWO 20m
data-mysql-3 Bound pvc-50043c45-b1c5-11e6-93fa-42010a800002 10Gi RWO 2m
data-mysql-4 Bound pvc-500a9957-b1c5-11e6-93fa-42010a800002 10Gi RWO 2m
만약 여분의 PVC들을 재사용하지 않을 것이라면, 이들을 삭제할 수 있다.
kubectl delete pvc data-mysql-3
kubectl delete pvc data-mysql-4
SELECT @@server_id
루프를 끝내기 위해, 터미널에 Ctrl+C를 입력하거나,
해당 명령을 다른 터미널에서 실행시키자.
kubectl delete pod mysql-client-loop --now
스테이트풀셋을 삭제한다. 이것은 파드들 또한 삭제할 것이다.
kubectl delete statefulset mysql
파드들의 삭제를 확인한다. 삭제가 완료되기까지 시간이 걸릴 수 있다.
kubectl get pods -l app=mysql
위와 같은 메세지가 나타나면 파드들이 삭제되었다는 것을 알 수 있다.
No resources found.
컨피그맵, 서비스, 그리고 퍼시스턴트볼륨클레임들을 삭제한다.
kubectl delete configmap,service,pvc -l app=mysql
만약 수동으로 퍼시스턴스볼륨들을 프로비저닝했다면, 수동으로 삭제하면서, 그 밑에 존재하는 리소스들을 또한 삭제해야 한다. 만약 동적 프로비저너를 사용했다면, 당신이 퍼시스턴트볼륨클레임으로 삭제하면 자동으로 퍼시스턴트볼륨을 삭제한다. 일부 (EBS나 PD와 같은) 동적 프로비저너들은 퍼시스턴트볼륨을 삭제 하면 그 뒤에 존재하는 리소스들도 삭제한다.
이 작업은 스테이트풀셋을 확장하는 방법을 보여준다. 스테이트풀셋 확장은 레플리카 수를 늘리거나 줄이는 것을 의미한다.
스테이트풀셋은 쿠버네티스 버전 1.5 이상에서만 사용할 수 있다.
쿠버네티스 버전을 확인하려면 kubectl version
을 실행한다.
모든 스테이트풀 애플리케이션이 제대로 확장되는 것은 아니다. 스테이트풀셋을 확장할지 여부가 확실하지 않은 경우에 자세한 내용은 스테이트풀셋 또는 스테이트풀셋 튜토리얼을 참조한다.
스테이트풀 애플리케이션 클러스터가 완전히 정상이라고 확신할 때만 확장을 수행해야 한다.
먼저 확장하려는 스테이트풀셋을 찾는다.
kubectl get statefulsets <stateful-set-name>
스테이트풀셋의 레플리카 수를 변경한다.
kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
대안으로 스테이트풀셋에 인플레이스 업데이트를 수행할 수 있다.
스테이트풀셋이 처음에 kubectl apply
로 생성된 경우,
스테이트풀셋 매니페스트의 .spec.replicas
를 업데이트한 다음 kubectl apply
를 수행한다.
kubectl apply -f <stateful-set-file-updated>
그렇지 않으면 kubectl edit
로 해당 필드를 편집한다.
kubectl edit statefulsets <stateful-set-name>
또는 kubectl patch
를 사용한다.
kubectl patch statefulsets <stateful-set-name> -p '{"spec":{"replicas":<new-replicas>}}'
스테이트풀셋에서 관리하는 스테이트풀 파드가 비정상인 경우에는 스테이트풀셋을 축소할 수 없다. 축소는 스테이트풀 파드가 실행되고 준비된 후에만 발생한다.
spec.replicas > 1인 경우 쿠버네티스는 비정상 파드의 원인을 결정할 수 없다. 영구적인 오류 또는 일시적인 오류의 결과일 수 있다. 일시적인 오류는 업그레이드 또는 유지 관리에 필요한 재시작으로 인해 발생할 수 있다.
영구적인 오류로 인해 파드가 비정상인 경우 오류를 수정하지 않고 확장하면 스테이트풀셋 멤버십이 올바르게 작동하는 데 필요한 특정 최소 레플리카 수 아래로 떨어지는 상태로 이어질 수 있다. 이로 인해 스테이트풀셋을 사용할 수 없게 될 수 있다.
일시적인 오류로 인해 파드가 비정상 상태이고 파드를 다시 사용할 수 있게 되면 일시적인 오류가 확장 또는 축소 작업을 방해할 수 있다. 일부 분산 데이터베이스에는 노드가 동시에 가입 및 탈퇴할 때 문제가 있다. 이러한 경우 애플리케이션 수준에서 확장 작업에 대해 추론하고 스테이트풀 애플리케이션 클러스터가 완전히 정상이라고 확신할 때만 확장을 수행하는 것이 좋다.
이 작업은 스테이트풀셋을 삭제하는 방법을 설명한다.
쿠버네티스에서 다른 리소스를 삭제하는 것과 같은 방식으로 스테이트풀셋을 삭제할 수 있다. kubectl delete
명령어를 사용하고 파일 또는 이름으로 스테이트풀셋을 지정하자.
kubectl delete -f <file.yaml>
kubectl delete statefulsets <statefulset-name>
스테이트풀셋 자체를 삭제한 후 연결된 헤드리스 서비스는 별도로 삭제해야 할 수도 있다.
kubectl delete service <service-name>
kubectl을 통해 스테이트풀셋을 삭제하면, 스테이트풀셋의 크기가 0으로 설정되고 이로 인해 스테이트풀셋에 포함된 모든 파드가 삭제된다. 파드가 아닌 스테이트풀셋만 삭제하려면, --cascade=orphan
옵션을 사용한다.
예시는 다음과 같다.
kubectl delete -f <file.yaml> --cascade=orphan
kubectl delete
에 --cascade=orphan
를 사용하면 스테이트풀셋 오브젝트가 삭제된 후에도 스테이트풀셋에 의해 관리된 파드는 남게 된다. 만약 파드가 app.kubernetes.io/name=MyApp
레이블을 갖고 있다면, 다음과 같이 파드를 삭제할 수 있다.
kubectl delete pods -l app.kubernetes.io/name=MyApp
스테이트풀셋의 파드들을 삭제하는 것이 연결된 볼륨을 삭제하는 것은 아니다. 이것은 볼륨을 삭제하기 전에 볼륨에서 데이터를 복사할 수 있는 기회를 준다. 파드가 종료된 후 PVC를 삭제하면 스토리지 클래스와 반환 정책에 따라 백업 퍼시스턴트볼륨 삭제가 트리거될 수 있다. 클레임 삭제 후 볼륨에 접근할 수 있다고 가정하면 안된다.
연결된 파드를 포함해서 스테이트풀셋의 모든 것을 삭제하기 위해 다음과 같이 일련의 명령을 실행한다.
grace=$(kubectl get pods <stateful-set-pod> --template '{{.spec.terminationGracePeriodSeconds}}')
kubectl delete statefulset -l app.kubernetes.io/name=MyApp
sleep $grace
kubectl delete pvc -l app.kubernetes.io/name=MyApp
위의 예에서 파드에는 app.kubernetes.io/name=MyApp
라는 레이블이 있다. 사용자에게 적절한 레이블로 대체하자.
스테이트풀셋의 일부 파드가 오랫동안 'Terminating' 또는 'Unknown' 상태에 있는 경우, apiserver에 수동적으로 개입하여 파드를 강제 삭제할 수도 있다. 이것은 잠재적으로 위험한 작업이다. 자세한 설명은 스테이트풀셋 파드 강제 삭제하기를 참고한다.
스테이트풀셋 파드 강제 삭제하기에 대해 더 알아보기.
이 페이지에서는 스테이트풀셋의 일부인 파드를 삭제하는 방법을 보여주고 이 과정에서 고려해야 할 사항을 설명한다.
정상적인 스테이트풀셋의 작동에서는 스테이트풀셋 파드를 강제로 삭제할 필요가 절대 없다. 스테이트풀셋 컨트롤러는 스테이트풀셋의 멤버 생성, 스케일링, 삭제를 담당한다. 서수 0부터 N-1까지 지정된 수의 파드가 활성 상태이고 준비되었는지 확인한다. 스테이트풀셋은 언제든지 클러스터에서 실행 중인 지정된 신원을 가진 최대 하나의 파드가 있는지 확인한다. 이를 스테이트풀셋에서 제공하는 최대 하나 의미론이라고 한다.
수동 강제 삭제는 스테이트풀셋 고유의 최대 하나 의미론을 위반할 가능성이 있으므로 주의해서 수행해야 한다. 스테이트풀셋은 안정적인 네트워크 신원과 안정적인 스토리지가 필요한 분산 클러스터 애플리케이션을 실행하는 데 사용할 수 있다. 이러한 애플리케이션은 종종 고정된 신원을 가진 고정된 수의 구성원 앙상블에 의존하는 구성을 가진다. 여러 구성원이 동일한 신원을 갖는 것은 재앙이 될 수 있으며 데이터 손실로 이어질 수 있다(예: 쿼럼 기반 시스템의 스플릿 브레인(split-brain) 시나리오).
다음 명령을 사용하여 파드를 단계적으로 삭제할 수 있다.
kubectl delete pods <pod>
위의 내용이 단계적인 종료로 이어지려면 파드가
pod.Spec.TerminationGracePeriodSeconds
를 0으로 지정하지 않아야 한다.
pod.Spec.TerminationGracePeriodSeconds
를 0초로 설정하는 관행은 안전하지 않으며
스테이트풀셋 파드에서 강력히 권장하지 않는다. 단계적 삭제는 안전하며 kubelet이 apiserver에서 이름을 삭제하기 전에 파드가
정상적으로 종료되도록 한다.
노드에 연결할 수 없는 경우 파드는 자동으로 삭제되지 않는다. 연결할 수 없는 노드에서 실행 중인 파드는 타임아웃 후에 'Terminating'이나 'Unknown' 상태가 된다.
사용자가 연결할 수 없는 노드에서 파드를 단계적으로 삭제하려고 하면 파드가 이러한 상태에 들어갈 수도 있다. 이러한 상태의 파드를 apiserver에서 제거할 수 있는 유일한 방법은 다음과 같다.
권장되는 모범 사례는 첫 번째 또는 두 번째 방법을 사용하는 것이다. 노드가 죽은 것으로 확인되면(예: 네트워크에서 영구적으로 연결이 끊기거나 전원이 꺼진 경우 등) 노드 오브젝트를 삭제한다. 노드에 네트워크 파티션이 있는 경우 이를 해결하거나 해결될 때까지 기다린다. 파티션이 복구되면 kubelet은 파드 삭제를 완료하고 apiserver에서 해당 이름을 해제한다.
일반적으로 시스템은 파드가 노드에서 더 이상 실행되지 않거나 노드가 관리자에 의해 삭제되면 삭제를 완료한다. 파드를 강제로 삭제하여 이를 재정의할 수 있다.
강제 삭제는 파드가 종료되었다는 kubelet의 확인을 기다리지 않는다. 강제 삭제가 파드를 죽이는 데 성공했는지 여부와 관계없이 즉시 apiserver에서 이름을 해제한다. 이렇게 하면 스테이트풀셋 컨트롤러가 동일한 신원으로 대체 파드를 생성할 수 있다. 이것은 여전히 실행 중인 파드와 중복될 수 있으며, 해당 파드가 여전히 스테이트풀셋의 다른 멤버와 통신할 수 있다면 스테이트풀셋이 보장하도록 설계된 최대 하나 의미론을 위반할 것이다.
스테이트풀셋 파드를 강제로 삭제하는 것은 문제의 파드가 스테이트풀셋의 다른 파드와 다시는 접촉하지 않으며 대체 생성을 위해 해당 이름을 안전하게 해제할 수 있다고 주장하는 것이다.
kubectl 버전 >= 1.5를 사용하여 파드를 강제로 삭제하려면, 다음을 수행한다.
kubectl delete pods <pod> --grace-period=0 --force
kubectl <= 1.4 버전을 사용하는 경우, --force
옵션을 생략하고 다음을 사용해야 한다.
kubectl delete pods <pod> --grace-period=0
이러한 명령 후에도 파드가 Unknown
상태에서 멈추면, 다음 명령을 사용하여 클러스터에서 파드를 제거한다.
kubectl patch pod <pod> -p '{"metadata":{"finalizers":null}}'
항상 관련된 위험에 대해 완전히 이해한 상태에서 주의 깊게 스테이트풀셋 파드의 강제 삭제를 수행한다.
스테이트풀셋 디버깅하기에 대해 더 알아보기.
쿠버네티스에서, HorizontalPodAutoscaler 는 워크로드 리소스(예: 디플로이먼트 또는 스테이트풀셋)를 자동으로 업데이트하며, 워크로드의 크기를 수요에 맞게 자동으로 스케일링하는 것을 목표로 한다.
수평 스케일링은 부하 증가에 대해 파드를 더 배치하는 것을 뜻한다. 이는 수직 스케일링(쿠버네티스에서는, 해당 워크로드를 위해 이미 실행 중인 파드에 더 많은 자원(예: 메모리 또는 CPU)를 할당하는 것)과는 다르다.
부하량이 줄어들고, 파드의 수가 최소 설정값 이상인 경우, HorizontalPodAutoscaler는 워크로드 리소스(디플로이먼트, 스테이트풀셋, 또는 다른 비슷한 리소스)에게 스케일 다운을 지시한다.
Horizontal Pod Autoscaling은 크기 조절이 불가능한 오브젝트(예: 데몬셋)에는 적용할 수 없다.
HorizontalPodAutoscaler는 쿠버네티스 API 자원 및 컨트롤러 형태로 구현되어 있다. HorizontalPodAutoscaler API 자원은 컨트롤러의 행동을 결정한다. 쿠버네티스 컨트롤 플레인 내에서 실행되는 HPA 컨트롤러는 평균 CPU 사용률, 평균 메모리 사용률, 또는 다른 커스텀 메트릭 등의 관측된 메트릭을 목표에 맞추기 위해 목표물(예: 디플로이먼트)의 적정 크기를 주기적으로 조정한다.
Horizontal Pod Autoscaling을 활용하는 연습 예제가 존재한다.
그림 1. HorizontalPodAutoscaler는 디플로이먼트 및 디플로이먼트의 레플리카셋의 크기를 조정한다.
쿠버네티스는 Horizontal Pod Autoscaling을
간헐적으로(intermittently) 실행되는
컨트롤 루프 형태로 구현했다(지속적인 프로세스가 아니다).
실행 주기는 kube-controller-manager
의
--horizontal-pod-autoscaler-sync-period
파라미터에 의해 설정된다(기본 주기는 15초이다).
각 주기마다, 컨트롤러 매니저는 각 HorizontalPodAutoscaler 정의에 지정된 메트릭에 대해 리소스 사용률을 질의한다.
컨트롤러 매니저는 scaleTargetRef
에 의해 정의된 타겟 리소스를 찾고 나서,
타겟 리소스의 .spec.selector
레이블을 보고 파드를 선택하며,
리소스 메트릭 API(파드 단위 리소스 메트릭 용) 또는
커스텀 메트릭 API(그 외 모든 메트릭 용)로부터 메트릭을 수집한다.
파드 단위 리소스 메트릭(예 : CPU)의 경우 컨트롤러는 HorizontalPodAutoscaler가 대상으로하는 각 파드에 대한 리소스 메트릭 API에서 메트릭을 가져온다. 그런 다음, 목표 사용률 값이 설정되면, 컨트롤러는 각 파드의 컨테이너에 대한 동등한 자원 요청을 퍼센트 단위로 하여 사용률 값을 계산한다. 대상 원시 값이 설정된 경우 원시 메트릭 값이 직접 사용된다. 그리고, 컨트롤러는 모든 대상 파드에서 사용된 사용률의 평균 또는 원시 값(지정된 대상 유형에 따라 다름)을 가져와서 원하는 레플리카의 개수를 스케일하는데 사용되는 비율을 생성한다.
파드의 컨테이너 중 일부에 적절한 리소스 요청이 설정되지 않은 경우, 파드의 CPU 사용률은 정의되지 않으며, 따라서 오토스케일러는 해당 메트릭에 대해 아무런 조치도 취하지 않는다. 오토스케일링 알고리즘의 작동 방식에 대한 자세한 내용은 아래 알고리즘 세부 정보 섹션을 참조하기 바란다.
파드 단위 사용자 정의 메트릭의 경우, 컨트롤러는 사용률 값이 아닌 원시 값을 사용한다는 점을 제외하고는 파드 단위 리소스 메트릭과 유사하게 작동한다.
오브젝트 메트릭 및 외부 메트릭의 경우, 문제의 오브젝트를 표현하는
단일 메트릭을 가져온다. 이 메트릭은 목표 값과
비교되어 위와 같은 비율을 생성한다. autoscaling/v2
API
버전에서는, 비교가 이루어지기 전에 해당 값을 파드의 개수로
선택적으로 나눌 수 있다.
HorizontalPodAutoscaler를 사용하는 일반적인 방법은
집약된 API(metrics.k8s.io
,
custom.metrics.k8s.io
, 또는 external.metrics.k8s.io
)로부터 메트릭을 가져오도록 설정하는 것이다.
metrics.k8s.io
API는 보통 메트릭 서버(Metrics Server)라는 애드온에 의해 제공되며,
Metrics Server는 별도로 실행해야 한다. 자원 메트릭에 대한 추가 정보는
Metrics Server를 참고한다.
메트릭 API를 위한 지원에서 위의 API들에 대한 안정성 보장 및 지원 상태를 확인할 수 있다.
HorizontalPodAutoscaler 컨트롤러는 스케일링을 지원하는 상응하는
워크로드 리소스(예: 디플로이먼트 및 스테이트풀셋)에 접근한다.
이들 리소스 각각은 scale
이라는 하위 리소스를 갖고 있으며,
이 하위 리소스는 레플리카의 수를 동적으로 설정하고 각각의 현재 상태를 확인할 수 있도록 하는 인터페이스이다.
쿠버네티스 API의 하위 리소스에 대한 일반적인 정보는 쿠버네티스 API 개념에서 확인할 수 있다.
가장 기본적인 관점에서, HorizontalPodAutoscaler 컨트롤러는 원하는(desired) 메트릭 값과 현재(current) 메트릭 값 사이의 비율로 작동한다.
원하는 레플리카 수 = ceil[현재 레플리카 수 * ( 현재 메트릭 값 / 원하는 메트릭 값 )]
예를 들어 현재 메트릭 값이 200m
이고 원하는 값이
100m
인 경우 200.0 / 100.0 == 2.0
이므로 복제본 수가 두 배가
된다. 만약 현재 값이 50m
이면, 50.0 / 100.0 == 0.5
이므로
복제본 수를 반으로 줄일 것이다. 컨트롤 플레인은 비율이 1.0(기본값이 0.1인
-horizontal-pod-autoscaler-tolerance
플래그를 사용하여
전역적으로 구성 가능한 허용 오차 내)에 충분히 가깝다면 스케일링을 건너 뛸 것이다.
targetAverageValue
또는 targetAverageUtilization
가 지정되면,
currentMetricValue
는 HorizontalPodAutoscaler의 스케일 목표
안에 있는 모든 파드에서 주어진 메트릭의 평균을 취하여 계산된다.
허용치를 확인하고 최종 값을 결정하기 전에,
컨트롤 플레인은 누락된 메트릭이 있는지,
그리고 몇 개의 파드가 Ready
인지도 고려한다.
삭제 타임스탬프가 설정된 모든 파드(파드에 삭제 타임스탬프가 있으면
셧다운/삭제 중임을 뜻한다)는 무시되며, 모든 실패한 파드는
버려진다.
특정 파드에 메트릭이 누락된 경우, 나중을 위해 처리를 미뤄두는데, 이와 같이 누락된 메트릭이 있는 모든 파드는 최종 스케일 량을 조정하는데 사용된다.
CPU를 스케일할 때, 파드가 아직 Ready되지 않았거나(여전히 초기화중이거나, unhealthy하여서) 또는 파드의 최신 메트릭 포인트가 준비되기 전이라면, 마찬가지로 해당 파드는 나중에 처리된다.
기술적 제약으로 인해, HorizontalPodAutoscaler 컨트롤러는
특정 CPU 메트릭을 나중에 사용할지 말지 결정할 때, 파드가 준비되는
시작 시간을 정확하게 알 수 없다. 대신, 파드가 아직 준비되지
않았고 시작 이후 짧은 시간 내에 파드가 준비 상태로
전환된다면, 해당 파드를 "아직 준비되지 않음(not yet ready)"으로 간주한다.
이 값은 --horizontal-pod-autoscaler-initial-readiness-delay
플래그로 설정되며, 기본값은 30초
이다. 일단 파드가 준비되고 시작된 후 구성 가능한 시간 이내이면,
준비를 위한 어떠한 전환이라도 이를 시작 시간으로 간주한다. 이
값은 --horizontal-pod-autoscaler-cpu-initialization-period
플래그로 설정되며
기본값은 5분이다.
현재 메트릭 값 / 원하는 메트릭 값
기본 스케일 비율은 나중에
사용하기로 되어 있거나 위에서 폐기되지 않은 남아있는 파드를 사용하여 계산된다.
누락된 메트릭이 있는 경우, 컨트롤 플레인은 파드가 스케일 다운의 경우 원하는 값의 100%를 소비하고 스케일 업의 경우 0%를 소비한다고 가정하여 평균을 보다 보수적으로 재계산한다. 이것은 잠재적인 스케일의 크기를 약화시킨다.
또한, 아직-준비되지-않은 파드가 있고, 누락된 메트릭이나 아직-준비되지-않은 파드가 고려되지 않고 워크로드가 스케일업 된 경우, 컨트롤러는 아직-준비되지-않은 파드가 원하는 메트릭의 0%를 소비한다고 보수적으로 가정하고 스케일 확장의 크기를 약화시킨다.
아직-준비되지-않은 파드나 누락된 메트릭을 고려한 후에, 컨트롤러가 사용률을 다시 계산한다. 새로 계산한 사용률이 스케일 방향을 바꾸거나, 허용 오차 내에 있으면, 컨트롤러가 스케일링을 건너뛴다. 그렇지 않으면, 새로 계산한 사용률를 이용하여 파드 수 변경 결정을 내린다.
평균 사용량에 대한 원래 값은 새로운 사용 비율이 사용되는 경우에도 아직-준비되지-않은 파드 또는 누락된 메트릭에 대한 고려없이 HorizontalPodAutoscaler 상태를 통해 다시 보고된다.
HorizontalPodAutoscaler에 여러 메트릭이 지정된 경우, 이 계산은
각 메트릭에 대해 수행된 다음 원하는 레플리카 수 중 가장
큰 값이 선택된다. 이러한 메트릭 중 어떠한 것도 원하는
레플리카 수로 변환할 수 없는 경우(예 : 메트릭 API에서 메트릭을
가져오는 중 오류 발생) 스케일을 건너뛴다.
이는 하나 이상의 메트릭이
현재 값보다 높은 desiredReplicas
을 제공하는 경우
HPA가 여전히 확장할 수 있음을 의미한다.
마지막으로, HPA가 목표를 스케일하기 직전에 스케일 권장 사항이
기록된다. 컨트롤러는 구성 가능한 창(window) 내에서 가장 높은 권장
사항을 선택하도록 해당 창 내의 모든 권장 사항을 고려한다. 이 값은 --horizontal-pod-autoscaler-downscale-stabilization
플래그를 사용하여 설정할 수 있고, 기본값은 5분이다.
즉, 스케일 다운이 점진적으로 발생하여 급격히 변동하는 메트릭 값의
영향을 완만하게 한다.
Horizontal Pod Autoscaler는
쿠버네티스 autoscaling
API 그룹의 API 리소스이다.
현재의 안정 버전은 autoscaling/v2
API 버전이며,
메모리와 커스텀 메트릭에 대한 스케일링을 지원한다.
autoscaling/v2
에서 추가된 새로운 필드는 autoscaling/v1
를 이용할 때에는
어노테이션으로 보존된다.
HorizontalPodAutoscaler API 오브젝트 생성시 지정된 이름이 유효한 DNS 서브도메인 이름인지 확인해야 한다. API 오브젝트에 대한 자세한 내용은 HorizontalPodAutoscaler 오브젝트에서 찾을 수 있다.
HorizontalPodAutoscaler를 사용하여 레플리카 그룹의 크기를 관리할 때, 측정하는 메트릭의 동적 특성 때문에 레플리카 수가 계속 자주 요동칠 수 있다. 이는 종종 thrashing 또는 flapping이라고 불린다. 이는 사이버네틱스 분야의 이력 현상(hysteresis) 개념과 비슷하다.
쿠버네티스는 디플로이먼트에 대한 롤링 업데이트를 지원한다.
이 경우, 디플로이먼트가 기저 레플리카셋을 알아서 관리한다.
디플로이먼트에 오토스케일링을 설정하려면,
각 디플로이먼트에 대한 HorizontalPodAutoscaler를 생성한다.
HorizontalPodAutoscaler는 디플로이먼트의 replicas
필드를 관리한다.
디플로이먼트 컨트롤러는 기저 레플리카셋에 replicas
값을 적용하여
롤아웃 과정 중/이후에 적절한 숫자까지 늘어나도록 한다.
오토스케일된 레플리카가 있는 스테이트풀셋의 롤링 업데이트를 수행하면, 스테이트풀셋이 직접 파드의 숫자를 관리한다(즉, 레플리카셋과 같은 중간 리소스가 없다).
모든 HPA 대상은 스케일링 대상에서 파드의 리소스 사용량을 기준으로 스케일링할 수 있다.
파드의 명세를 정의할 때는 cpu
및 memory
와 같은 리소스 요청을
지정해야 한다. 이것은 리소스 사용률을 결정하는 데 사용되며 HPA 컨트롤러에서 대상을
스케일링하거나 축소하는 데 사용한다. 리소스 사용률 기반 스케일링을 사용하려면 다음과 같은 메트릭 소스를
지정해야 한다.
type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
이 메트릭을 사용하면 HPA 컨트롤러는 스케일링 대상에서 파드의 평균 사용률을 60%로 유지한다. 사용률은 파드의 요청된 리소스에 대한 현재 리소스 사용량 간의 비율이다. 사용률 계산 및 평균 산출 방법에 대한 자세한 내용은 알고리즘을 참조한다.
Kubernetes v1.20 [alpha]
HorizontalPodAutoscaler API는 대상 리소스를 스케일링하기 위해 HPA가 파드 집합에서 개별 컨테이너의 리소스 사용량을 추적할 수 있는 컨테이너 메트릭 소스도 지원한다. 이를 통해 특정 파드에서 가장 중요한 컨테이너의 스케일링 임계값을 구성할 수 있다. 예를 들어 웹 애플리케이션 프로그램과 로깅 사이드카가 있는 경우 사이드카 컨테이너와 해당 리소스 사용을 무시하고 웹 애플리케이션의 리소스 사용을 기준으로 스케일링할 수 있다.
대상 리소스를 다른 컨테이너 세트를 사용하여 새 파드 명세를 갖도록 수정하는 경우 새로 추가된 컨테이너도 스케일링에 사용해야 한다면 HPA 사양을 수정해야 한다. 메트릭 소스에 지정된 컨테이너가 없거나 파드의 하위 집합에만 있는 경우 해당 파드는 무시되고 권장 사항이 다시 계산된다. 계산에 대한 자세한 내용은 알고리즘을 을 참조한다. 컨테이너 리소스를 오토스케일링에 사용하려면 다음과 같이 메트릭 소스를 정의한다.
type: ContainerResource
containerResource:
name: cpu
container: application
target:
type: Utilization
averageUtilization: 60
위의 예에서 HPA 컨트롤러는 모든 파드의 application
컨테이너에 있는 CPU의 평균 사용률이
60%가 되도록 대상을 조정한다.
HorizontalPodAutoscaler가 추적하는 컨테이너의 이름을 변경하는 경우 특정 순서로 변경을 수행하여 변경 사항이 적용되는 동안 스케일링을 계속 사용할 수 있고 효율적으로 유지할 수 있다. 컨테이너를 정의하는 리소스(예: 배포)를 업데이트하기 전에 연결된 HPA를 업데이트하여 새 컨테이너 이름과 이전 컨테이너 이름을 모두 추적해야 한다. 이러한 방식으로 HPA는 업데이트 프로세스 전반에 걸쳐 스케일링 권장 사항을 계산할 수 있다.
컨테이너 이름 변경을 워크로드 리소스로 롤아웃한 후에는 HPA 사양에서 이전 컨테이너 이름을 제거하여 정리한다.
Kubernetes v1.23 [stable]
(이전에는 autoscaling/v2beta2
API 버전이 이 기능을 베타 기능으로 제공했었다.)
autoscaling/v2beta2
API 버전을 사용하는 경우,
(쿠버네티스 또는 어느 쿠버네티스 구성 요소에도 포함되어 있지 않은)
커스텀 메트릭을 기반으로 스케일링을 수행하도록 HorizontalPodAutoscaler를 구성할 수 있다.
이 경우 HorizontalPodAutoscaler 컨트롤러가 이러한 커스텀 메트릭을 쿠버네티스 API로부터 조회한다.
요구 사항에 대한 정보는 메트릭 API를 위한 지원을 참조한다.
Kubernetes v1.23 [stable]
(이전에는 autoscaling/v2beta2
API 버전이 이 기능을 베타 기능으로 제공했었다.)
autoscaling/v2
API 버전을 사용하는 경우,
HorizontalPodAutoscaler는 스케일링에 사용할 복수의 메트릭을 설정할 수 있다.
이 경우 HorizontalPodAutoscaler 컨트롤러가 각 메트릭을 확인하고 해당 단일 메트릭에 대한 새로운 스케일링 크기를 제안한다.
HorizontalPodAutoscaler는 새롭게 제안된 스케일링 크기 중 가장 큰 값을 선택하여 워크로드 사이즈를
조정한다(이 값이 이전에 설정한 '총 최대값(overall maximum)'보다는 크지 않을 때에만).
기본적으로 HorizontalPodAutoscaler 컨트롤러는 일련의 API에서 메트릭을 검색한다. 이러한 API에 접속하려면 클러스터 관리자는 다음을 확인해야 한다.
API 애그리게이션 레이어 활성화
해당 API 등록:
리소스 메트릭의 경우, 일반적으로 이것은 메트릭-서버가 제공하는 metrics.k8s.io
API이다.
클러스터 애드온으로 실행할 수 있다.
사용자 정의 메트릭의 경우, 이것은 custom.metrics.k8s.io
API이다. 메트릭 솔루션 공급 업체에서 제공하는 "어댑터" API 서버에서 제공한다.
사용 가능한 쿠버네티스 메트릭 어댑터가 있는지 확인하려면 사용하고자 하는 메트릭 파이프라인을 확인한다.
외부 메트릭의 경우, 이것은 external.metrics.k8s.io
API이다. 위에 제공된 사용자 정의 메트릭 어댑터에서 제공될 수 있다.
이런 다양한 메트릭 경로와 각각의 다른 점에 대한 상세 내용은 관련 디자인 제안서인 HPA V2, custom.metrics.k8s.io, external.metrics.k8s.io를 참조한다.
어떻게 사용하는지에 대한 예시는 커스텀 메트릭 사용하는 작업 과정과 외부 메트릭스 사용하는 작업 과정을 참조한다.
Kubernetes v1.23 [stable]
(이전에는 autoscaling/v2beta2
API 버전이 이 기능을 베타 기능으로 제공했었다.)
v2
버전의 HorizontalPodAutoscaler API를 사용한다면,
behavior
필드(API 레퍼런스 참고)를
사용하여 스케일업 동작과 스케일다운 동작을 별도로 구성할 수 있다.
각 방향에 대한 동작은 behavior
필드 아래의
scaleUp
/ scaleDown
를 설정하여 지정할 수 있다.
안정화 윈도우 를 명시하여 스케일링 목적물의 레플리카 수 흔들림을 방지할 수 있다. 스케일링 정책을 이용하여 스케일링 시 레플리카 수 변화 속도를 조절할 수도 있다.
스펙의 behavior
섹션에 하나 이상의 스케일링 폴리시를 지정할 수 있다.
폴리시가 여러 개 지정된 경우 가장 많은 양의 변경을
허용하는 정책이 기본적으로 선택된 폴리시이다. 다음 예시는 스케일 다운 중 이
동작을 보여준다.
behavior:
scaleDown:
policies:
- type: Pods
value: 4
periodSeconds: 60
- type: Percent
value: 10
periodSeconds: 60
periodSeconds
는 폴리시가 참(true)으로 유지되어야 하는 기간을 나타낸다.
첫 번째 정책은 (파드들) 이 1분 내에 최대 4개의 레플리카를 스케일 다운할 수 있도록 허용한다.
두 번째 정책은 비율 로 현재 레플리카의 최대 10%를 1분 내에 스케일 다운할 수 있도록 허용한다.
기본적으로 가장 많은 변경을 허용하는 정책이 선택되기에 두 번째 정책은 파드의 레플리카 수가 40개를 초과하는 경우에만 사용된다. 레플리카가 40개 이하인 경우 첫 번째 정책이 적용된다. 예를 들어 80개의 레플리카가 있고 대상을 10개의 레플리카로 축소해야 하는 경우 첫 번째 단계에서 8개의 레플리카가 스케일 다운 된다. 레플리카의 수가 72개일 때 다음 반복에서 파드의 10%는 7.2 이지만, 숫자는 8로 올림된다. 오토스케일러 컨트롤러의 각 루프에서 변경될 파드의 수는 현재 레플리카의 수에 따라 재계산된다. 레플리카의 수가 40 미만으로 떨어지면 첫 번째 폴리시 (파드들) 가 적용되고 한번에 4개의 레플리카가 줄어든다.
확장 방향에 대해 selectPolicy
필드를 확인하여 폴리시 선택을 변경할 수 있다.
레플리카의 수를 최소로 변경할 수 있는 폴리시를 선택하는 최소(Min)
로 값을 설정한다.
값을 Disabled
로 설정하면 해당 방향으로 스케일링이 완전히
비활성화된다.
안정화 윈도우는 스케일링에 사용되는 메트릭이 계속 변동할 때 레플리카 수의 흔들림을 제한하기 위해 사용된다. 오토스케일링 알고리즘은 이전의 목표 상태를 추론하고 워크로드 수의 원치 않는 변화를 방지하기 위해 이 안정화 윈도우를 활용한다.
예를 들어, 다음 예제 스니펫에서, scaleDown
에 대해 안정화 윈도우가 설정되었다.
behavior:
scaleDown:
stabilizationWindowSeconds: 300
메트릭 관측 결과 스케일링 목적물이 스케일 다운 되어야 하는 경우, 알고리즘은 이전에 계산된 목표 상태를 확인하고, 해당 구간에서 계산된 값 중 가장 높은 값을 사용한다. 위의 예시에서, 이전 5분 동안의 모든 목표 상태가 고려 대상이 된다.
이를 통해 동적 최대값(rolling maximum)을 근사화하여, 스케일링 알고리즘이 빠른 시간 간격으로 파드를 제거하고 바로 다시 동일한 파드를 재생성하는 현상을 방지할 수 있다.
사용자 지정 스케일링을 사용하기 위해서 모든 필드를 지정하지 않아도 된다. 사용자 정의가 필요한 값만 지정할 수 있다. 이러한 사용자 지정 값은 기본값과 병합된다. 기본값은 HPA 알고리즘의 기존 동작과 동일하다.
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 100
periodSeconds: 15
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
- type: Pods
value: 4
periodSeconds: 15
selectPolicy: Max
안정화 윈도우의 스케일링 다운의 경우 300 초 (또는 제공된
경우--horizontal-pod-autoscaler-downscale-stabilization
플래그의 값)이다. 스케일링 다운에서는 현재
실행 중인 레플리카의 100%를 제거할 수 있는 단일 정책만 있으며, 이는 스케일링
대상을 최소 허용 레플리카로 축소할 수 있음을 의미한다.
스케일링 업에는 안정화 윈도우가 없다. 메트릭이 대상을 스케일 업해야 한다고 표시된다면 대상이 즉시 스케일 업된다.
두 가지 폴리시가 있다. HPA가 정상 상태에 도달 할 때까지 15초 마다
4개의 파드 또는 현재 실행 중인 레플리카의 100% 가 추가된다.
사용자 지정 다운스케일 안정화 윈도우를 1분 동안 제공하기 위해 다음 동작이 HPA에 추가된다.
behavior:
scaleDown:
stabilizationWindowSeconds: 60
HPA에 의해 파드가 제거되는 속도를 분당 10%로 제한하기 위해 다음 동작이 HPA에 추가된다.
behavior:
scaleDown:
policies:
- type: Percent
value: 10
periodSeconds: 60
분당 제거되는 파드 수가 5를 넘지 않도록 하기 위해, 크기가 5로 고정된 두 번째 축소
정책을 추가하고, selectPolicy
를 최소로 설정하면 된다. selectPolicy
를 Min
으로 설정하면
자동 스케일러가 가장 적은 수의 파드에 영향을 주는 정책을 선택함을 의미한다.
behavior:
scaleDown:
policies:
- type: Percent
value: 10
periodSeconds: 60
- type: Pods
value: 5
periodSeconds: 60
selectPolicy: Min
selectPolicy
의 Disabled
값은 주어진 방향으로의 스케일링을 끈다.
따라서 다운 스케일링을 방지하기 위해 다음 폴리시가 사용된다.
behavior:
scaleDown:
selectPolicy: Disabled
Horizontal Pod Autoscaler는 모든 API 리소스와 마찬가지로 kubectl
에 의해 표준 방식으로 지원된다.
kubectl create
커맨드를 사용하여 새로운 오토스케일러를 만들 수 있다.
kubectl get hpa
로 오토스케일러 목록을 조회할 수 있고, kubectl describe hpa
로 세부 사항을 확인할 수 있다.
마지막으로 kubectl delete hpa
를 사용하여 오토스케일러를 삭제할 수 있다.
또한 Horizontal Pod Autoscaler를 생성할 수 있는 kubectl autoscale
이라는 특별한 명령이 있다.
예를 들어 kubectl autoscale rs foo --min=2 --max=5 --cpu-percent=80
을
실행하면 레플리카셋 foo 에 대한 오토스케일러가 생성되고, 목표 CPU 사용률은 80 %
,
그리고 2와 5 사이의 레플리카 개수로 설정된다.
HPA 구성 자체를 변경할 필요없이 대상에 대한
HPA를 암시적으로 비활성화할 수 있다. 대상의 의도한
레플리카 수가 0으로 설정되고, HPA의 최소 레플리카 수가 0 보다 크면, 대상의
의도한 레플리카 수 또는 HPA의 최소 레플리카 수를 수동으로 조정하여
다시 활성화할 때까지 HPA는 대상 조정을
중지한다(그리고 ScalingActive
조건 자체를 false
로 설정).
HPA가 활성화되어 있으면, 디플로이먼트, 스테이트풀셋 모두 또는 둘 중 하나의
매니페스트에서
spec.replicas
의 값을 삭제하는 것이 바람직하다.
이렇게 적용하지 않으면, (예를 들어 kubectl apply -f deployment.yaml
명령으로)
오브젝트에 변경이 생길 때마다 쿠버네티스가 파드의 수를
spec.replicas
에 기재된 값으로 조정할 것이다.
이는 바람직하지 않으며 HPA가 활성화된 경우에 문제가 될 수 있다.
spec.replicas
값을 제거하면 1회성으로 파드 숫자가 줄어들 수 있는데,
이는 이 키의 기본값이 1이기 때문이다(레퍼런스:
디플로이먼트 레플리카).
값을 업데이트하면, 파드 1개를 제외하고 나머지 파드가 종료 절차에 들어간다.
이후의 디플로이먼트 애플리케이션은 정상적으로 작동하며 롤링 업데이트 구성도 의도한 대로 동작한다.
이러한 1회성 저하를 방지하는 방법이 존재하며,
디플로이먼트 수정 방법에 따라 다음 중 한 가지 방법을 선택한다.
kubectl apply edit-last-applied deployment/<디플로이먼트_이름>
spec.replicas
를 삭제한다. 저장하고 에디터를 종료하면, kubectl
이
업데이트 사항을 적용한다. 이 단계에서 파드 숫자가 변경되지는 않는다.spec.replicas
를 삭제할 수 있다.
소스 코드 관리 도구를 사용하고 있다면, 변경 사항을 추적할 수 있도록
변경 사항을 커밋하고 추가 필요 단계를 수행한다.kubectl apply -f deployment.yaml
를 실행할 수 있다.서버 쪽에 적용하기를 수행하려면, 정확히 이러한 사용 사례를 다루고 있는 소유권 이전하기 가이드라인을 참조한다.
클러스터에 오토스케일링을 구성한다면, Cluster Autoscaler와 같은 클러스터 수준의 오토스케일러 사용을 고려해 볼 수 있다.
HorizontalPodAutoscaler에 대한 더 많은 정보는 아래를 참고한다.
kubectl autoscale
문서를 확인한다.HorizontalPodAutoscaler(약어: HPA)는 워크로드 리소스(예: 디플로이먼트 또는 스테이트풀셋)를 자동으로 업데이트하며, 워크로드의 크기를 수요에 맞게 자동으로 스케일링하는 것을 목표로 한다.
수평 스케일링은 부하 증가에 대해 파드를 더 배치하는 것을 뜻한다. 이는 수직 스케일링(쿠버네티스에서는, 해당 워크로드를 위해 이미 실행 중인 파드에 더 많은 자원(예: 메모리 또는 CPU)를 할당하는 것)과는 다르다.
부하량이 줄어들고, 파드의 수가 최소 설정값 이상인 경우, HorizontalPodAutoscaler는 워크로드 리소스(디플로이먼트, 스테이트풀셋, 또는 다른 비슷한 리소스)에게 스케일 다운을 지시한다.
이 문서는 예제 웹 앱의 크기를 자동으로 조절하도록 HorizontalPodAutoscaler를 설정하는 예시를 다룬다. 이 예시 워크로드는 PHP 코드를 실행하는 아파치 httpd이다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: 1.23. 버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
이전 버전의
쿠버네티스를 사용하고 있다면, 해당 버전의 문서를
참고한다(사용 가능한 문서의 버전 참고).
이 예제를 실행하기 위해, 클러스터에 Metrics Server가 배포 및 구성되어 있어야 한다. 쿠버네티스 Metrics Server는 클러스터의 kubelet으로부터 리소스 메트릭을 수집하고, 수집한 메트릭을 쿠버네티스 API를 통해 노출시키며, 메트릭 수치를 나타내는 새로운 종류의 리소스를 추가하기 위해 APIService를 사용할 수 있다.
Metrics Server를 실행하는 방법을 보려면 metrics-server 문서를 참고한다.
HorizontalPodAutoscaler 시연을 위해, hpa-example
이미지를 사용하여 컨테이너를 실행하는 디플로이먼트를 시작하고,
다음의 매니페스트를 사용하여 디플로이먼트를
서비스로 노출한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: php-apache
spec:
selector:
matchLabels:
run: php-apache
replicas: 1
template:
metadata:
labels:
run: php-apache
spec:
containers:
- name: php-apache
image: registry.k8s.io/hpa-example
ports:
- containerPort: 80
resources:
limits:
cpu: 500m
requests:
cpu: 200m
---
apiVersion: v1
kind: Service
metadata:
name: php-apache
labels:
run: php-apache
spec:
ports:
- port: 80
selector:
run: php-apache
이를 위해, 다음의 명령어를 실행한다.
kubectl apply -f https://k8s.io/examples/application/php-apache.yaml
deployment.apps/php-apache created
service/php-apache created
이제 서비스가 동작중이므로,
kubectl
을 사용하여 오토스케일러를 생성한다. 이를 위해
kubectl autoscale 서브커맨드를 사용할 수 있다.
아래에서는 첫 번째 단계에서 만든 php-apache 디플로이먼트 파드의 개수를 1부터 10 사이로 유지하는 Horizontal Pod Autoscaler를 생성하는 명령어를 실행할 것이다.
간단히 이야기하면, HPA 컨트롤러는
평균 CPU 사용량을 50%로 유지하기 위해 (디플로이먼트를 업데이트하여) 레플리카의 개수를 늘리고 줄인다.
그러면 디플로이먼트는 레플리카셋을 업데이트하며(이는 모든 쿠버네티스 디플로이먼트의 동작 방식 중 일부이다),
레플리카셋은 자신의 .spec
필드의 변경 사항에 따라 파드를 추가하거나 제거한다.
kubectl run
으로 각 파드는 200 밀리코어를 요청하므로, 평균 CPU 사용은 100 밀리코어이다.
알고리즘에 대한 세부 사항은
알고리즘 세부 정보를 참고한다.
HorizontalPodAutoscaler를 생성한다.
kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
horizontalpodautoscaler.autoscaling/php-apache autoscaled
다음을 실행하여, 새로 만들어진 HorizontalPodAutoscaler의 현재 상태를 확인할 수 있다.
# "hpa" 또는 "horizontalpodautoscaler" 둘 다 사용 가능하다.
kubectl get hpa
출력은 다음과 같다.
NAME REFERENCE TARGET MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache/scale 0% / 50% 1 10 1 18s
(HorizontalPodAutoscalers 이름이 다르다면, 이미 기존에 존재하고 있었다는 뜻이며, 보통은 문제가 되지 않는다.)
아직 서버로 요청을 보내는 클라이언트가 없기 때문에, 현재 CPU 사용량이 0%임을 확인할 수 있다.
(TARGET
열은 디플로이먼트에 의해 제어되는 파드들의 평균을 나타낸다)
다음으로, 부하가 증가함에 따라 오토스케일러가 어떻게 반응하는지를 살펴볼 것이다. 이를 위해, 클라이언트 역할을 하는 다른 파드를 실행할 것이다. 클라이언트 파드 안의 컨테이너가 php-apache 서비스에 쿼리를 보내는 무한 루프를 실행한다.
# 부하 생성을 유지하면서 나머지 스텝을 수행할 수 있도록,
# 다음의 명령을 별도의 터미널에서 실행한다.
kubectl run -i --tty load-generator --rm --image=busybox:1.28 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"
이제 아래 명령을 실행한다.
# 준비가 되면, 관찰을 마치기 위해 Ctrl+C를 누른다.
kubectl get hpa
1분 쯤 지나면, 다음과 같이 CPU 부하가 올라간 것을 볼 수 있다.
NAME REFERENCE TARGET MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache/scale 305% / 50% 1 10 1 3m
그리고 다음과 같이 레플리카의 수가 증가한 것도 볼 수 있다.
NAME REFERENCE TARGET MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache/scale 305% / 50% 1 10 7 3m
CPU 사용률이 305%까지 증가하였다. 결과적으로, 디플로이먼트의 레플리카 개수가 7개까지 증가하였다.
kubectl get deployment php-apache
HorizontalPodAutoscaler를 조회했을 때와 동일한 레플리카 수를 확인할 수 있다.
NAME READY UP-TO-DATE AVAILABLE AGE
php-apache 7/7 7 7 19m
본 예제를 마무리하기 위해 부하를 중단시킨다.
busybox
파드를 띄운 터미널에서,
<Ctrl> + C
로 부하 발생을 중단시킨다.
그런 다음 (몇 분 후에) 결과를 확인한다.
# 준비가 되면, 관찰을 마치기 위해 Ctrl+C를 누른다.
kubectl get hpa php-apache --watch
출력은 다음과 같다.
NAME REFERENCE TARGET MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache/scale 0% / 50% 1 10 1 11m
디플로이먼트도 스케일 다운 했음을 볼 수 있다.
kubectl get deployment php-apache
NAME READY UP-TO-DATE AVAILABLE AGE
php-apache 1/1 1 1 27m
CPU 사용량이 0으로 떨어져서, HPA가 자동으로 레플리카의 개수를 1로 줄였다.
php-apache
디플로이먼트를 오토스케일링할 때,
autoscaling/v2
API 버전을 사용하여 추가적인 메트릭을 제공할 수 있다.
첫 번째로, autoscaling/v2
형식으로 HorizontalPodAutoscaler YAML 파일을 생성한다.
kubectl get hpa php-apache -o yaml > /tmp/hpa-v2.yaml
에디터로 /tmp/hpa-v2.yaml
파일을 열면, 다음과 같은 YAML을 확인할 수 있다.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
status:
observedGeneration: 1
lastScaleTime: <some-time>
currentReplicas: 1
desiredReplicas: 1
currentMetrics:
- type: Resource
resource:
name: cpu
current:
averageUtilization: 0
averageValue: 0
targetCPUUtilizationPercentage
필드가 metrics
배열로 대체되었다.
CPU 사용량 메트릭은 resource metric 으로 파드 컨테이너 자원의 백분율로 표현된다.
CPU 외에 다른 메트릭을 지정할 수 있는데, 기본적으로 지원되는 다른 메트릭은 메모리뿐이다.
이 자원들은 한 클러스터에서 다른 클러스터로 이름을 변경할 수 없으며,
metrics.k8s.io
API가 가용한 경우 언제든지 사용할 수 있어야 한다.
또한, Utilization
대신 AverageValue
의 target
타입을,
그리고 target.averageUtilization
대신 target.averageValue
로 설정하여
자원 메트릭을 퍼센트 대신 값으로 명시할 수 있다.
파드 메트릭과 오브젝트 메트릭 두 가지의 사용자 정의 메트릭 이 있다. 파드 메트릭과 오브젝트 메트릭. 이 메트릭은 클러스터에 특화된 이름을 가지고 있으며, 더 고급화된 클러스터 모니터링 설정이 필요하다.
이러한 대체 메트릭 타입중 첫 번째는 파드 메트릭 이다.
이 메트릭은 파드들을 설명하고, 파드들 간의 평균을 내며, 대상 값과 비교하여 레플리카 개수를 결정한다.
이것들은 AverageValue
의 target
만을 지원한다는 것을 제외하면, 자원 메트릭과 매우 유사하게 동작한다.
파드 메트릭은 이처럼 메트릭 블록을 사용하여 정의된다.
type: Pods
pods:
metric:
name: packets-per-second
target:
type: AverageValue
averageValue: 1k
두 번째 대체 메트릭 타입은 오브젝트 메트릭 이다.
이 메트릭은 파드를 기술하는 대신에 동일한 네임스페이스 내에 다른 오브젝트를 표현한다.
이 메트릭은 반드시 오브젝트로부터 가져올 필요는 없다. 단지 오브젝트를 기술할 뿐이다.
오브젝트 메트릭은 Value
과 AverageValue
의 target
타입을 지원한다.
Value
를 사용할 경우 대상은 API로부터 반환되는 메트릭과 직접 비교된다.
AverageValue
를 사용할 경우, 대상 값과 비교되기 이전에 사용자 정의 메트릭 API로부터 반환된 값은 파드의 개수로 나눠진다.
다음은 requests-per-second
메트릭을 YAML로 기술한 예제이다.
type: Object
object:
metric:
name: requests-per-second
describedObject:
apiVersion: networking.k8s.io/v1
kind: Ingress
name: main-route
target:
type: Value
value: 2k
이러한 메트릭 블록을 여러 개 제공하면, HorizontalPodAutoscaler는 각 메트릭을 차례로 고려한다. HorizontalPodAutoscaler는 각 메트릭에 대해 제안된 레플리카 개수를 계산하고, 그중 가장 높은 레플리카 개수를 선정한다.
예를 들어, 네트워크 트래픽 메트릭을 수집하는 모니터링 시스템이 있는 경우,
kubectl edit
명령어를 이용하여 다음과 같이 정의를 업데이트 할 수 있다.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
- type: Pods
pods:
metric:
name: packets-per-second
target:
type: AverageValue
averageValue: 1k
- type: Object
object:
metric:
name: requests-per-second
describedObject:
apiVersion: networking.k8s.io/v1
kind: Ingress
name: main-route
target:
type: Value
value: 10k
status:
observedGeneration: 1
lastScaleTime: <some-time>
currentReplicas: 1
desiredReplicas: 1
currentMetrics:
- type: Resource
resource:
name: cpu
current:
averageUtilization: 0
averageValue: 0
- type: Object
object:
metric:
name: requests-per-second
describedObject:
apiVersion: networking.k8s.io/v1
kind: Ingress
name: main-route
current:
value: 10k
이후, HorizontalPodAutoscaler는 각 파드가 요청 된 약 50%의 CPU 사용률을 소모하는지, 초당 1000 패킷을 처리하는지, 메인-루트 인그레스 뒤의 모든 파드들이 초당 10000 요청을 처리하는지 확인한다.
많은 메트릭 파이프라인들을 사용하면 이름 또는 labels 이라 불리는 추가적인 식별자로 메트릭을 설명할 수 있다.
그리고, 모든 비 자원 메트릭 타입(파드, 오브젝트 그리고 아래 기술된 외부 타입)에 대해,
메트릭 파이프라인으로 전달되는 추가 레이블 셀렉터를 지정할 수 있다.
예를 들면, verb
레이블로 http_requests
메트릭을 수집하는 경우,
다음과 같이 메트릭 블록을 지정하여 GET 요청에 대해 크기를 조정할 수 있다.
type: Object
object:
metric:
name: http_requests
selector: {matchLabels: {verb: GET}}
이 셀렉터는 쿠버네티스의 레이블 셀렉터와 동일한 문법이다.
모니터링 파이프라인은 네임과 셀렉터가 여러 시리즈와 일치하는 경우,
해당 여러 시리즈를 단일 값으로 축소하는 방법을 결정한다.
셀렉터는 부가적인 속성이며,
대상 오브젝트(Pods
타입의 대상 파드, Object
타입으로 기술된 오브젝트)가 아닌 메트릭을 선택할 수 없다.
쿠버네티스 위에서 동작하는 애플리케이션은, 쿠버네티스 클러스터의 어떤 오브젝트와도 관련이 없는 메트릭에 기반하여 오토스케일링을 할 수도 있다. 예로, 쿠버네티스 네임스페이스와 관련이 없는 서비스를 기초로한 메트릭을 들 수 있다. 쿠버네티스 버전 1.10 포함 이후 버전에서, 외부 메트릭 을 사용하여 이러한 유스케이스를 해결할 수 있다.
외부 메트릭 사용시, 먼저 모니터링 시스템에 대한 이해가 있어야 한다.
이 설치는 사용자 정의 메트릭과 유사하다.
외부 메트릭을 사용하면 모니터링 시스템의 사용 가능한 메트릭에 기반하여 클러스터를 오토스케일링 할 수 있다.
위의 예제처럼 name
과 selector
를 갖는 metric
블록을 명시하고,
Object
대신에 External
메트릭 타입을 사용한다.
만일 여러 개의 시계열이 metricSelector
와 일치하면, HorizontalPodAutoscaler가 값의 합을 사용한다.
외부 메트릭들은 Value
와 AverageValue
대상 타입을 모두 지원하고,
Object
타입을 사용할 때와 똑같이 동작한다.
예를 들면 애플리케이션이 호스팅 된 대기열 서비스에서 작업을 처리하는 경우, 다음과 같이 HorizontalPodAutoscaler 매니퍼스트에 30개의 미해결 태스크 당 한 개의 워커를 지정하도록 추가할 수 있다.
- type: External
external:
metric:
name: queue_messages_ready
selector:
matchLabels:
queue: "worker_tasks"
target:
type: AverageValue
averageValue: 30
가능하다면, 외부 메트릭 대신 사용자 정의 메트릭 대상 타입을 사용하길 권장한다. 왜냐하면, 클러스터 관리자가 사용자 정의 메트릭 API를 보안관점에서 더 쉽게 보호할 수 있기 때문이다. 외부 메트릭 API는 잠재적으로 어떠한 메트릭에도 접근할 수 있기에, 클러스터 관리자는 API를 노출시킬때 신중해야 한다.
HorizontalPodAutoscaler의 autoscaling/v2
형식을 사용하면,
HorizontalPodAutoscaler에서 쿠버네티스가 설정한 상태 조건 을 확인할 수 있다.
이 상태 조건들은 HorizontalPodAutoscaler가 스케일을 할 수 있는지,
어떤 방식으로든 제한되어 있는지 여부를 나타낸다.
이 조건은 status.conditions
에 나타난다.
HorizontalPodAutoscaler에 영향을 주는 조건을 보기 위해 kubectl describe hpa
를 사용할 수 있다.
kubectl describe hpa cm-test
Name: cm-test
Namespace: prom
Labels: <none>
Annotations: <none>
CreationTimestamp: Fri, 16 Jun 2017 18:09:22 +0000
Reference: ReplicationController/cm-test
Metrics: ( current / target )
"http_requests" on pods: 66m / 500m
Min replicas: 1
Max replicas: 4
ReplicationController pods: 1 current / 1 desired
Conditions:
Type Status Reason Message
---- ------ ------ -------
AbleToScale True ReadyForNewScale the last scale time was sufficiently old as to warrant a new scale
ScalingActive True ValidMetricFound the HPA was able to successfully calculate a replica count from pods metric http_requests
ScalingLimited False DesiredWithinRange the desired replica count is within the acceptable range
Events:
이 HorizontalPodAutoscaler 경우, 건강 상태의 여러 조건들을 볼 수 있다.
첫 번째 AbleToScale
는 HPA가 스케일을 가져오고 업데이트할 수 있는지,
백 오프 관련 조건으로 스케일링이 방지되는지 여부를 나타낸다.
두 번째 ScalingActive
는 HPA가 활성화되어 있는지(즉 대상 레플리카 개수가 0이 아닌지),
원하는 스케일을 계산할 수 있는지 여부를 나타낸다. 만약 False
인 경우,
일반적으로 메트릭을 가져오는 데 문제가 있다.
마지막으로, 마지막 조건인 ScalingLimited
는
원하는 스케일 한도가 HorizontalPodAutoscaler의 최대/최소값으로 제한돼있음을 나타낸다.
이는 HorizontalPodAutoscaler에서 레플리카의 개수 제한을 최대/최소값으로 올리거나 낮추려는 것이다.
HorizontalPodAutoscaler와 메트릭 API에서 모든 메트릭은
쿠버네티스에서 사용하는
수량 숫자 표기법을 사용한다.
예를 들면, 10500m
수량은 10진법 10.5
으로 쓰인다.
메트릭 API들은 가능한 경우 접미사 없이 정수를 반환하며,
일반적으로 수량을 밀리단위로 반환한다.
10진수로 표현했을때, 1
과 1500m
또는 1
과 1.5
로 메트릭 값을 나타낼 수 있다.
HorizontalPodAutoscaler를 생성하기 위해 kubectl autoscale
명령어를 사용하지 않고,
명시적으로 다음 매니페스트를 사용하여 만들 수 있다.
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 50
다음으로, 아래의 명령어를 실행하여 오토스케일러를 생성한다.
kubectl create -f https://k8s.io/examples/application/hpa/php-apache.yaml
horizontalpodautoscaler.autoscaling/php-apache created
이 페이지는 파드 내에서 쿠버네티스 API에 접근하는 방법을 보여준다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
파드 내에서 API에 접근할 때, API 서버를 찾아 인증하는 것은 위에서 설명한 외부 클라이언트 사례와 약간 다르다.
파드에서 쿠버네티스 API를 사용하는 가장 쉬운 방법은 공식 클라이언트 라이브러리 중 하나를 사용하는 것이다. 이러한 라이브러리는 API 서버를 자동으로 감지하고 인증할 수 있다.
파드 내에서, 쿠버네티스 API에 연결하는 권장 방법은 다음과 같다.
Go 클라이언트의 경우, 공식 Go 클라이언트 라이브러리를 사용한다.
rest.InClusterConfig()
기능은 API 호스트 검색과 인증을 자동으로 처리한다.
여기 예제를 참고한다.
Python 클라이언트의 경우, 공식 Python 클라이언트 라이브러리를 사용한다.
config.load_incluster_config()
기능은 API 호스트 검색과 인증을 자동으로 처리한다.
여기 예제를 참고한다.
사용할 수 있는 다른 라이브러리가 많이 있다. 클라이언트 라이브러리 페이지를 참고한다.
각각의 경우, 파드의 서비스 어카운트 자격 증명은 API 서버와 안전하게 통신하는 데 사용된다.
파드에서 실행되는 동안, 쿠버네티스 apiserver는 default
네임스페이스에서 kubernetes
라는
서비스를 통해 접근할 수 있다. 따라서, 파드는 kubernetes.default.svc
호스트 이름을 사용하여 API 서버를 쿼리할 수 있다. 공식 클라이언트 라이브러리는
이를 자동으로 수행한다.
API 서버를 인증하는 권장 방법은
서비스 어카운트 자격 증명을 사용하는 것이다.
기본적으로, 파드는 서비스 어카운트와 연결되어 있으며,
해당 서비스 어카운트에 대한 자격 증명(토큰)은
해당 파드에 있는 각 컨테이너의 파일시스템 트리의
/var/run/secrets/kubernetes.io/serviceaccount/token
에 있다.
사용 가능한 경우, 인증서 번들은 각 컨테이너의
파일시스템 트리의 /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
에 배치되며,
API 서버의 제공 인증서를 확인하는 데 사용해야 한다.
마지막으로, 네임스페이스가 지정된 API 작업에 사용되는 기본 네임스페이스는 각 컨테이너의
/var/run/secrets/kubernetes.io/serviceaccount/namespace
에 있는 파일에 배치된다.
공식 클라이언트 라이브러리 없이 API를 쿼리하려면, 파드에서
새 사이드카 컨테이너의 명령으로
kubectl proxy
를 실행할 수 있다. 이런 식으로, kubectl proxy
는
API를 인증하고 이를 파드의 localhost
인터페이스에 노출시켜서, 파드의
다른 컨테이너가 직접 사용할 수 있도록 한다.
인증 토큰을 API 서버에 직접 전달하여 kubectl 프록시 사용을 피할 수 있다. 내부 인증서는 연결을 보호한다.
# 내부 API 서버 호스트 이름을 가리킨다
APISERVER=https://kubernetes.default.svc
# 서비스어카운트(ServiceAccount) 토큰 경로
SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
# 이 파드의 네임스페이스를 읽는다
NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
# 서비스어카운트 베어러 토큰을 읽는다
TOKEN=$(cat ${SERVICEACCOUNT}/token)
# 내부 인증 기관(CA)을 참조한다
CACERT=${SERVICEACCOUNT}/ca.crt
# TOKEN으로 API를 탐색한다
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api
출력은 다음과 비슷하다.
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "10.0.1.149:443"
}
]
}
크론잡을 이용하여 잡(Job)을 시간 기반의 스케줄에 따라 실행할 수 있다. 이러한 자동화된 잡은 리눅스 또는 유닉스 시스템의 크론 작업처럼 실행된다.
크론 잡은 백업을 수행하거나 이메일을 보내는 것과 같이 주기적이고 반복적인 작업들을 생성하는 데 유용하다. 크론 잡은 시스템 사용이 적은 시간에 잡을 스케줄하려는 경우처럼 특정 시간에 개별 작업을 스케줄할 수도 있다.
크론 잡에는 제한 사항과 특이점이 있다. 예를 들어, 특정 상황에서는 하나의 크론 잡이 여러 잡을 생성할 수 있다. 따라서, 잡은 멱등성을 가져야 한다.
제한 사항에 대한 자세한 내용은 크론잡을 참고한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
크론 잡은 구성 파일이 필요하다. 다음은 1분마다 간단한 데모 작업을 실행하는 크론잡 매니페스트다.
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "* * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox:1.28
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
다음 명령을 사용하여 크론잡 예제를 실행한다.
kubectl create -f https://k8s.io/examples/application/job/cronjob.yaml
출력 결과는 다음과 비슷하다.
cronjob.batch/hello created
크론 잡을 생성한 후, 다음 명령을 사용하여 상태를 가져온다.
kubectl get cronjob hello
출력 결과는 다음과 비슷하다.
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
hello */1 * * * * False 0 <none> 10s
명령의 결과에서 알 수 있듯이, 크론 잡은 아직 잡을 스케줄하거나 실행하지 않았다. 약 1분 내로 잡이 생성되는지 확인한다.
kubectl get jobs --watch
출력 결과는 다음과 비슷하다.
NAME COMPLETIONS DURATION AGE
hello-4111706356 0/1 0s
hello-4111706356 0/1 0s 0s
hello-4111706356 1/1 5s 5s
이제 "hello" 크론 잡에 의해 스케줄된 실행 중인 작업을 확인했다. 잡 감시를 중지한 뒤에 크론 잡이 다시 스케줄되었는지를 확인할 수 있다.
kubectl get cronjob hello
출력 결과는 다음과 비슷하다.
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
hello */1 * * * * False 0 50s 75s
크론 잡 hello
가 LAST SCHEDULE
열에 명시된 시간에 잡을 성공적으로 스케줄링한 것을 볼 수 있다.
현재는 0개의 활성 잡이 있고, 이는 작업이 완료되었거나 실패했음을 의미한다.
이제, 마지막으로 스케줄된 잡이 생성한 파드를 찾고 생성된 파드 중 하나의 표준 출력을 확인한다.
# "hello-4111706356"을 사용자의 시스템에 있는 잡 이름으로 바꾼다
pods=$(kubectl get pods --selector=job-name=hello-4111706356 --output=jsonpath={.items[*].metadata.name})
파드의 로그를 출력한다.
kubectl logs $pods
출력 결과는 다음과 비슷하다.
Fri Feb 22 11:02:09 UTC 2019
Hello from the Kubernetes cluster
더 이상 크론 잡이 필요하지 않으면, kubectl delete cronjob <cronjob name>
명령을 사용해서 삭제한다.
kubectl delete cronjob hello
크론 잡을 삭제하면 생성된 모든 잡과 파드가 제거되고 추가 잡 생성이 중지된다. 가비지(garbage) 수집에서 잡 제거에 대해 상세한 내용을 읽을 수 있다.
다른 모든 쿠버네티스 오브젝트들과 마찬가지로,
크론잡은 apiVersion
, kind
그리고 metadata
필드가 반드시 필요하다.
쿠버네티스 오브젝트 및 그 매니페스트 다루기에 대한
자세한 내용은 리소스 관리하기와
kubectl을 사용하여 리소스 관리하기 문서를 참고한다.
크론잡(CronJob)의 각 매니페스트에는 .spec
섹션도 필요하다.
.spec.schedule
은 .spec
의 필수 필드이다.
이 필드는 0 * * * *
또는 @hourly
와 같은 크론 형식의 문자열을 받아,
해당 잡이 생성 및 실행될 스케줄 시간으로 설정한다.
이 형식은 확장된 "Vixie cron" 스텝(step) 값도 포함한다. 이 내용은 FreeBSD 매뉴얼에 설명되어 있다.
스텝 값은 범위(range)와 함께 사용할 수 있다. 범위 뒤에
/<number>
를 지정하여 범위 내에서 숫자만큼의 값을 건너뛴다. 예를 들어, 시간 필드에0-23/2
를 사용하여 매 2시간마다 명령 실행을 지정할 수 있다(V7 표준의 대안은0,2,4,6,8,10,12,14,16,18,20,22
이다). 별표(asterisk) 뒤에 붙이는 스텝도 허용되며, "2시간마다"라고 지정하고 싶으면, 간단히*/2
를 사용하면 된다.
?
)는 별표 *
와 동일한 의미를 가지며,
주어진 필드에 대해 사용할 수 있는 모든 값을 나타낸다..spec.jobTemplate
은 잡에 대한 템플릿이며, 이것은 필수 필드다.
이것은 중첩되어(nested) 있고 apiVersion
이나 kind
가 없다는 것을 제외하면,
잡과 정확히 같은 스키마를 가진다.
잡 .spec
을 작성하는 것에 대한 내용은 잡 명세 작성하기를 참고한다.
.spec.startingDeadlineSeconds
필드는 선택 사항이다.
어떤 이유로든 스케줄된 시간을 놓친 경우 잡의 시작 기한을 초 단위로 나타낸다.
기한이 지나면, 크론 잡이 잡을 시작하지 않는다.
이러한 방식으로 기한을 맞추지 못한 잡은 실패한 작업으로 간주된다.
이 필드를 지정하지 않으면, 잡에 기한이 없다.
.spec.startingDeadlineSeconds
필드가 (null이 아닌 값으로) 설정되어 있다면,
크론잡 컨트롤러는 예상 잡 생성 시각과 현재 시각의 차이를 측정하고,
시각 차이가 설정한 값보다 커지면 잡 생성 동작을 스킵한다.
예를 들어, 200
으로 설정되었다면,
예상 잡 생성 시각으로부터 200초까지는 잡이 생성될 수 있다.
.spec.concurrencyPolicy
필드도 선택 사항이다.
이것은 이 크론 잡에 의해 생성된 잡의 동시 실행을 처리하는 방법을 지정한다.
명세는 다음의 동시성 정책 중 하나만 지정할 수 있다.
Allow
(기본값): 크론 잡은 동시에 실행되는 잡을 허용한다.Forbid
: 크론 잡은 동시 실행을 허용하지 않는다.
새로운 잡을 실행할 시간이고 이전 잡 실행이 아직 완료되지 않은 경우, 크론 잡은 새로운 잡 실행을 건너뛴다.Replace
: 새로운 잡을 실행할 시간이고 이전 잡 실행이 아직 완료되지 않은 경우,
크론 잡은 현재 실행 중인 잡 실행을 새로운 잡 실행으로 대체한다.참고로 동시성 정책은 동일한 크론 잡에 의해 생성된 잡에만 적용된다. 크론 잡이 여러 개인 경우, 각각의 잡은 항상 동시에 실행될 수 있다.
.spec.suspend
필드도 선택 사항이다.
true
로 설정되면, 모든 후속 실행이 일시 정지된다.
이 설정은 이미 시작된 실행에는 적용되지 않는다.
기본값은 false이다.
.spec.suspend
가 true
에서 false
로 변경되면,
누락된 잡들이 즉시 스케줄된다..spec.successfulJobsHistoryLimit
와 .spec.failedJobsHistoryLimit
필드는 선택 사항이다.
이들 필드는 기록을 보관해야 하는 완료 및 실패한 잡의 개수를 지정한다.
기본적으로, 각각 3과 1로 설정된다.
한도를 0
으로 설정하는 것은 잡 완료 후에 해당 잡 유형의 기록을 보관하지 않는다는 것이다.
이 예제에서는, 여러 병렬 워커 프로세스를 활용해 쿠버네티스 잡(Job)을 실행한다.
이 예제에서는, 각 파드가 생성될 때 작업 대기열에서 하나의 작업 단위를 선택하여, 완료하고, 대기열에서 삭제하고, 종료한다.
이 예제에서의 단계에 대한 개요는 다음과 같다.
기본적이고, 병렬 작업이 아닌, 잡의 사용법에 대해 잘 알고 있어야 한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
이 예시에서는 RabbitMQ를 사용하지만, 다른 AMQP 유형의 메시지 서비스를 사용하도록 예시를 조정할 수 있다.
실제로 사용할 때는, 클러스터에 메시지 대기열 서비스를 한 번 구축하고서, 여러 많은 잡이나 오래 동작하는 서비스에 재사용할 수 있다.
다음과 같이 RabbitMQ를 시작한다.
kubectl create -f https://raw.githubusercontent.com/kubernetes/kubernetes/release-1.3/examples/celery-rabbitmq/rabbitmq-service.yaml
service "rabbitmq-service" created
kubectl create -f https://raw.githubusercontent.com/kubernetes/kubernetes/release-1.3/examples/celery-rabbitmq/rabbitmq-controller.yaml
replicationcontroller "rabbitmq-controller" created
이 문서에서는 celery-rabbitmq 예제에 나오는 정도로만 rabbitmq를 사용한다.
이제, 메시지 대기열을 이용해 실험할 수 있다. 임시 대화형 파드를 만들어 그 위에 도구들을 설치하고, 대기열을 실험해본다.
먼저 임시 대화형 파드를 만든다.
# 임시 대화형 컨테이너를 만든다.
kubectl run -i --tty temp --image ubuntu:18.04
Waiting for pod default/temp-loe07 to be running, status is Pending, pod ready: false
... [ previous line repeats several times .. hit return when it stops ] ...
참고로 파드 이름과 명령 프롬프트는 위와 다를 수 있다.
다음으로 amqp-tools
를 설치하여 메시지 대기열을 활용할 수 있게 한다.
# 도구들을 설치한다.
root@temp-loe07:/# apt-get update
.... [ lots of output ] ....
root@temp-loe07:/# apt-get install -y curl ca-certificates amqp-tools python dnsutils
.... [ lots of output ] ....
후에, 이 패키지들을 포함하는 도커 이미지를 만든다.
다음으로, rabbitmq 서비스를 발견할 수 있는지 확인한다.
# rabbitmq-service가 쿠버네티스로부터 주어진 DNS 이름을 갖는다.
root@temp-loe07:/# nslookup rabbitmq-service
Server: 10.0.0.10
Address: 10.0.0.10#53
Name: rabbitmq-service.default.svc.cluster.local
Address: 10.0.147.152
# 주소는 다를 수 있다.
만약 Kube-DNS가 적절히 구축되지 않았다면, 전 단계 작업이 작동하지 않을 수 있다. 환경 변수를 통해서도 서비스 IP를 찾을 수 있다.
# env | grep RABBIT | grep HOST
RABBITMQ_SERVICE_SERVICE_HOST=10.0.147.152
# 주소는 다를 수 있다.
다음으로 대기열을 생성하고, 메시지를 발행하고 사용할 수 있는지 확인한다.
# 다음 줄에서, rabbitmq-service는 rabbitmq-service에 접근할 수 있는
# 호스트네임이다. 5672는 rabbitmq의 표준 포트이다.
root@temp-loe07:/# export BROKER_URL=amqp://guest:guest@rabbitmq-service:5672
# 만약 전 단계에서 "rabbitmq-service"가 주소로 변환되지 않는다면,
# 이 커맨드를 대신 사용하면 된다.
# root@temp-loe07:/# BROKER_URL=amqp://guest:guest@$RABBITMQ_SERVICE_SERVICE_HOST:5672
# 이제 대기열을 생성한다.
root@temp-loe07:/# /usr/bin/amqp-declare-queue --url=$BROKER_URL -q foo -d
foo
# 대기열에 메시지를 하나 발행한다.
root@temp-loe07:/# /usr/bin/amqp-publish --url=$BROKER_URL -r foo -p -b Hello
# 다시 메시지를 돌려받는다.
root@temp-loe07:/# /usr/bin/amqp-consume --url=$BROKER_URL -q foo -c 1 cat && echo
Hello
root@temp-loe07:/#
마지막 커맨드에서, amqp-consume
도구는 대기열로부터 하나의 메시지를
받고(-c 1
), 그 메시지를 임의의 명령 표준입력으로 전달한다. 이 경우에는, cat
프로그램이 표준입력으로부터 받은 값을 출력하고, echo가 캐리지 리턴을 더해주어
출력 결과가 보여진다.
이제 몇 가지 "작업"으로 대기열을 채운다. 이 예제에서의 작업은 문자열을 출력하는 것이다.
실제로 사용할 때는, 메시지의 내용이 다음과 같을 수 있다.
실제로는, 잡의 모든 파드에서 읽기-전용 모드로 필요한 큰 데이터가 있다면, 일반적으로 그 데이터를 NFS와 같은 공유 파일시스템에 넣고 모든 파드에 읽기 전용으로 마운트하거나, 파드 안에 있는 프로그램이 기본적으로 HDFS와 같은 클러스터 파일시스템으로부터 데이터를 불러들인다.
본 예제에서는, 대기열을 만들고 amqp 커맨드라인 도구를 이용해 대기열을 채울 것이다. 실제로는, amqp 라이브러리를 이용해 대기열을 채우는 프로그램을 작성하게 된다.
/usr/bin/amqp-declare-queue --url=$BROKER_URL -q job1 -d
job1
for f in apple banana cherry date fig grape lemon melon
do
/usr/bin/amqp-publish --url=$BROKER_URL -r job1 -p -b $f
done
8개의 메시지로 대기열을 채웠다.
이제 잡으로 실행할 이미지를 만들 준비가 되었다.
amqp-consume
유틸리티를 이용해 대기열로부터 메시지를 읽고,
실제 프로그램을 실행해 볼 것이다.
여기에 아주 간단한 예제 프로그램이 있다.
#!/usr/bin/env python
# 표준 출력만 출력하고 10초 동안 대기한다.
import sys
import time
print("Processing " + sys.stdin.readlines()[0])
time.sleep(10)
스크립트에 실행 권한을 준다.
chmod +x worker.py
이제 이미지를 빌드한다. 만약 소스 트리 안에서 작업하고
있다면, examples/job/work-queue-1
로 디렉터리를 옮긴다.
아니면, 임시 디렉터리를 만들고, 그 디렉터리로 옮긴다.
Dockerfile과
worker.py를 다운로드한다.
위 두 경우 모두, 다음의 명령을 이용해 이미지를 빌드한다.
docker build -t job-wq-1 .
도커 허브를 이용하기 위해, 앱 이미지를
사용자의 username으로 태깅하고 아래의 명령어를 이용해 허브에 푸시한다.
<username>
을 사용자의 허브 username으로 대체한다.
docker tag job-wq-1 <username>/job-wq-1
docker push <username>/job-wq-1
만약 구글 컨테이너
레지스트리를 이용하고 있다면,
앱 이미지를 사용자의 프로젝트 ID를 이용해 태깅하고, GCR에 푸시한다.
<proejct>
부분을 사용자의 프로젝트 ID로 대체한다.
docker tag job-wq-1 gcr.io/<project>/job-wq-1
gcloud docker -- push gcr.io/<project>/job-wq-1
다음은 잡 정의이다. 잡의 사본을 만들고 위에서 정한 이름에 맞게
이미지를 수정하고, 파일 이름을 ./job.yaml
이라 정한다.
apiVersion: batch/v1
kind: Job
metadata:
name: job-wq-1
spec:
completions: 8
parallelism: 2
template:
metadata:
name: job-wq-1
spec:
containers:
- name: c
image: gcr.io/<project>/job-wq-1
env:
- name: BROKER_URL
value: amqp://guest:guest@rabbitmq-service:5672
- name: QUEUE
value: job1
restartPolicy: OnFailure
이 예시에서는, 각 파드가 대기열로부터 얻은 하나의 아이템을 수행하고 종료한다.
그래서, 잡의 완료 횟수가 완료된 작업 아이템의 숫자에 대응한다.
예시에서 .spec.completions: 8
이라 정한 것도, 대기열에 8개의 아이템을 넣었기 때문이다.
이제 잡을 실행한다.
kubectl apply -f ./job.yaml
아래와 같이 시간 제한(timeout)을 설정하고, 잡이 성공할 때까지 기다린다.
# 조건명은 대소문자를 구분하지 않는다.
kubectl wait --for=condition=complete --timeout=300s job/job-wq-1
잡을 확인한다.
kubectl describe jobs/job-wq-1
Name: job-wq-1
Namespace: default
Selector: controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
Labels: controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
job-name=job-wq-1
Annotations: <none>
Parallelism: 2
Completions: 8
Start Time: Wed, 06 Sep 2017 16:42:02 +0800
Pods Statuses: 0 Running / 8 Succeeded / 0 Failed
Pod Template:
Labels: controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
job-name=job-wq-1
Containers:
c:
Image: gcr.io/causal-jigsaw-637/job-wq-1
Port:
Environment:
BROKER_URL: amqp://guest:guest@rabbitmq-service:5672
QUEUE: job1
Mounts: <none>
Volumes: <none>
Events:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
───────── ──────── ───── ──── ───────────── ────── ────── ───────
27s 27s 1 {job } Normal SuccessfulCreate Created pod: job-wq-1-hcobb
27s 27s 1 {job } Normal SuccessfulCreate Created pod: job-wq-1-weytj
27s 27s 1 {job } Normal SuccessfulCreate Created pod: job-wq-1-qaam5
27s 27s 1 {job } Normal SuccessfulCreate Created pod: job-wq-1-b67sr
26s 26s 1 {job } Normal SuccessfulCreate Created pod: job-wq-1-xe5hj
15s 15s 1 {job } Normal SuccessfulCreate Created pod: job-wq-1-w2zqe
14s 14s 1 {job } Normal SuccessfulCreate Created pod: job-wq-1-d6ppa
14s 14s 1 {job } Normal SuccessfulCreate Created pod: job-wq-1-p17e0
모든 파드가 성공했다. 야호.
이러한 접근은 "워커" 프로그램을 작업 대기열에 맞게 수정하지 않아도 된다는 장점이 있다.
이 접근을 이용하려면, 메시지 대기열 서비스를 실행해야만 한다. 만약 메시지 대기열 서비스를 실행하는 게 불편하다면, 다른 잡 패턴을 고려해볼 수 있다.
이 접근은 모든 작업 아이템에 대해 파드를 생성한다. 만약 작업 아이템이 오직 몇 초밖에 걸리지 않는 작업이라면, 매 작업마다 파드를 생성하는 것은 아주 큰 오버헤드를 더할 수 있다. 하나의 파드가 여러 작업 아이템을 수행하는 이 예제를 고려해보자.
이 예제에서는, amqp-consume
유틸리티를 이용해 대기열로부터 메시지를 읽어
실제 프로그램을 실행했다. 이러면 메시지 대기열을 이용하기 위해 프로그램을 수정하지
않아도 된다는 장점이 있다.
다른 예제는
클라이언트 라이브러리를 이용해 작업 대기열과 소통하는 방법을 보여준다.
만약 작업 완료 수가 대기열에 있는 아이템의 숫자보다 적게 설정되면, 모든 아이템 처리되지 않는다.
만약 작업 완료 수가 큐에 있는 아이템의 숫자보다 많게 설정되면, 대기열에 있는 아이템이 모두 처리되어도, 잡이 완료됐다고 표시되지 않고, 메시지를 기다리는 과정에서 막히는 파드를 추가적으로 실행시킨다.
이 패턴에서는 경쟁 상태(race)가 잘 나타나지 않는다. 만약 amqp-consume 명령으로부터 메시지가 인정되는 시간과 컨테이너가 성공적으로 종료되는 시간 사이에 컨테이너가 종료되거나, kubelet이 api-server에게 파드가 성공했음을 알리기 전에 노드가 비정상적으로 종료되면, 대기열의 모든 아이템이 처리되었다 해도, 잡이 완료되었다고 표시되지 않는다.
이 예에서는, 지정된 파드에서 여러 병렬 워커 프로세스가 있는 쿠버네티스 잡(Job)을 실행한다.
이 예에서는, 각 파드가 생성될 때, 작업 대기열에서 하나의 작업 단위를 선택하여, 처리하고, 대기열이 비워질 때까지 반복한다.
이 예에서의 단계에 대한 개요는 다음과 같다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
잡의 기본적이고, 병렬 작업이 아닌, 사용법에 대해 잘 알고 있어야 한다.
이 문서의 예시에서는, 단순함을 위해, Redis의 단일 인스턴스를 시작한다. Redis를 확장 가능하고 중복적으로 배포하는 예에 대해서는 Redis 예시를 참고한다.
다음 파일을 직접 다운로드할 수도 있다.
이제 몇 가지 "작업"으로 대기열을 채운다. 이 예제의 작업은 문자열을 출력하는 것이다.
Redis CLI를 실행하기 위한 임시 대화형 파드를 시작한다.
kubectl run -i --tty temp --image redis --command "/bin/sh"
Waiting for pod default/redis2-c7h78 to be running, status is Pending, pod ready: false
Hit enter for command prompt
이제 엔터 키를 누르고, redis CLI를 시작하고, 몇몇 작업 항목이 포함된 목록을 생성한다.
# redis-cli -h redis
redis:6379> rpush job2 "apple"
(integer) 1
redis:6379> rpush job2 "banana"
(integer) 2
redis:6379> rpush job2 "cherry"
(integer) 3
redis:6379> rpush job2 "date"
(integer) 4
redis:6379> rpush job2 "fig"
(integer) 5
redis:6379> rpush job2 "grape"
(integer) 6
redis:6379> rpush job2 "lemon"
(integer) 7
redis:6379> rpush job2 "melon"
(integer) 8
redis:6379> rpush job2 "orange"
(integer) 9
redis:6379> lrange job2 0 -1
1) "apple"
2) "banana"
3) "cherry"
4) "date"
5) "fig"
6) "grape"
7) "lemon"
8) "melon"
9) "orange"
자, 키 job2
가 있는 목록이 작업 대기열이 된다.
참고: Kube DNS를 올바르게 설정하지 않은 경우, 위 블록의
첫 번째 단계를 redis-cli -h $REDIS_SERVICE_HOST
로 변경해야 할 수 있다.
이제 실행할 이미지를 만들 준비가 되었다.
redis 클라이언트와 함께 python 워커 프로그램을 사용하여 메시지 큐에서 메시지를 읽는다.
rediswq.py(다운로드)라는 간단한 Redis 작업 대기열 클라이언트 라이브러리가 제공된다.
잡의 각 파드에 있는 "워커" 프로그램은 작업 대기열 클라이언트 라이브러리를 사용하여 작업을 가져온다. 다음은 워커 프로그램이다.
#!/usr/bin/env python
import time
import rediswq
host="redis"
# Uncomment next two lines if you do not have Kube-DNS working.
# import os
# host = os.getenv("REDIS_SERVICE_HOST")
q = rediswq.RedisWQ(name="job2", host=host)
print("Worker with sessionID: " + q.sessionID())
print("Initial queue state: empty=" + str(q.empty()))
while not q.empty():
item = q.lease(lease_secs=10, block=True, timeout=2)
if item is not None:
itemstr = item.decode("utf-8")
print("Working on " + itemstr)
time.sleep(10) # Put your actual work here instead of sleep.
q.complete(item)
else:
print("Waiting for work")
print("Queue empty, exiting")
worker.py
,
rediswq.py
및
Dockerfile
파일을 다운로드할 수 있고, 그런 다음
이미지를 만들 수도 있다.
docker build -t job-wq-2 .
도커 허브(Docker Hub)를 위해, 아래 명령으로
사용자의 username과 앱 이미지에 태그하고 허브에 푸시한다. <username>
을
사용자의 허브 username으로 바꾼다.
docker tag job-wq-2 <username>/job-wq-2
docker push <username>/job-wq-2
공용 저장소로 푸시하거나 개인 저장소에 접근할 수 있도록 클러스터를 구성해야 한다.
Google Container
Registry를 사용하는 경우,
사용자의 프로젝트 ID로 앱 이미지에 태그를 지정하고 GCR로 푸시한다. <project>
를
사용자의 프로젝트 ID로 바꾼다.
docker tag job-wq-2 gcr.io/<project>/job-wq-2
gcloud docker -- push gcr.io/<project>/job-wq-2
다음은 잡 정의이다.
apiVersion: batch/v1
kind: Job
metadata:
name: job-wq-2
spec:
parallelism: 2
template:
metadata:
name: job-wq-2
spec:
containers:
- name: c
image: gcr.io/myproject/job-wq-2
restartPolicy: OnFailure
사용자 자신의 경로로 gcr.io/myproject
를
변경하려면 잡 템플릿을 편집해야 한다.
이 예에서, 각 파드는 대기열의 여러 항목에 대해 작업한 다음 더 이상 항목이 없을 때 종료된다. 워커는 작업 대기열이 비어있을 때를 감지하고 잡 컨트롤러는 작업 대기열에 대해 알지 못하기 때문에, 작업이 완료되면 워커에게 신호를 보낸다. 워커는 성공적으로 종료하여 대기열이 비어 있음을 알린다. 따라서, 워커가 성공적으로 종료하자마자, 컨트롤러는 작업이 완료되었음을 인식하고, 파드가 곧 종료된다. 따라서, 잡 완료 횟수를 1로 설정했다. 잡 컨트롤러는 다른 파드도 완료될 때까지 기다린다.
이제 잡을 실행한다.
kubectl apply -f ./job.yaml
이제 조금 기다린 다음, 잡을 확인한다.
kubectl describe jobs/job-wq-2
Name: job-wq-2
Namespace: default
Selector: controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
Labels: controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
job-name=job-wq-2
Annotations: <none>
Parallelism: 2
Completions: <unset>
Start Time: Mon, 11 Jan 2016 17:07:59 -0800
Pods Statuses: 1 Running / 0 Succeeded / 0 Failed
Pod Template:
Labels: controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
job-name=job-wq-2
Containers:
c:
Image: gcr.io/exampleproject/job-wq-2
Port:
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
33s 33s 1 {job-controller } Normal SuccessfulCreate Created pod: job-wq-2-lglf8
아래와 같이 시간 제한(timeout)을 설정하고, 잡이 성공할 때까지 기다린다.
# 조건명은 대소문자를 구분하지 않는다.
kubectl wait --for=condition=complete --timeout=300s job/job-wq-2
kubectl logs pods/job-wq-2-7r7b2
Worker with sessionID: bbd72d0a-9e5c-4dd6-abf6-416cc267991f
Initial queue state: empty=False
Working on banana
Working on date
Working on lemon
보다시피, 파드 중 하나가 여러 작업 항목을 처리했다.
대기열 서비스를 실행하거나 작업 대기열을 사용하도록 컨테이너를 수정하는 것이 불편한 경우, 다른 잡 패턴 중 하나를 고려할 수 있다.
만약 실행할 백그라운드 처리 작업의 연속 스트림이 있는 경우,
ReplicaSet
이 있는 백그라운드 워커를 실행하는 것과,
https://github.com/resque/resque와 같은
백그라운드 처리 라이브러리를 실행하는 것이 좋다.
Kubernetes v1.24 [stable]
이 예제에서는, 여러 병렬 워커(worker) 프로세스를 활용해 쿠버네티스 잡(Job)을 실행한다. 각 워커는 각 파드에서 실행되는 서로 다른 컨테이너다. 파드에는 컨트롤 플레인이 자동으로 설정한 인덱스 번호 가 부여되며, 이를 통해 각 파드는 전체 태스크 중 어느 부분을 수행해야 할 지 식별할 수 있다.
파드 인덱스는 10진수 값을 문자열로 표현한 어노테이션
batch.kubernetes.io/job-completion-index
를 통해
사용할 수 있다. 컨테이너화된 태스크 프로세스가 이 인덱스 정보를 가져갈 수 있도록,
다운워드(downward) API
메커니즘을 사용하여 어노테이션의 값을 발행할 수 있다.
편의상, 컨트롤 플레인은 downward API를 자동 설정하여
JOB_COMPLETION_INDEX
환경변수에 인덱스를 노출할 수 있도록 한다.
이 예제에서의 단계에 대한 개요는 다음과 같다.
Indexed
) 잡을 시작한다.기본적이고, 병렬 작업이 아닌, 잡의 사용법에 대해 잘 알고 있어야 한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.21. 버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
워커 프로그램에서 작업 항목에 접근하기 위한 몇 가지 선택지가 있다.
JOB_COMPLETION_INDEX
환경변수를 읽는다. 잡
컨트롤러 는
이 환경변수를 완료 인덱스 정보를 갖고 있는 어노테이션에 자동
연결한다.예를 들어, 3번째 방법을 선택했으며 rev 유틸리티를 실행한다고 가정하면, 이 프로그램은 파일을 인자로 받아 그 내용을 거꾸로 출력한다.
rev data.txt
rev
툴은
busybox
컨테이너 이미지 내에서 실행할 것이다.
예제에 불과하므로, 각 파드는 아주 작은 작업(짧은 문자열 거꾸로 뒤집기)만을 수행한다. 실제 워크로드에서는 예를 들어 장면 정보를 바탕으로 60초 길이의 영상을 제작하는 태스크에 해당하는 잡을 생성할 수도 있다. 영상 렌더링 잡의 각 작업 항목은 영상 클립의 특정 프레임을 렌더링하는 것이다. 색인된 완료는 잡에 포함된 각 파드가 클립의 시작 지점부터 프레임 수를 세어 어느 프레임을 렌더링하고 발행해야 할 지 알고 있음을 의미한다.
다음은 색인된(Indexed
) 완료 모드를 사용하는 잡 매니페스트의 예이다.
apiVersion: batch/v1
kind: Job
metadata:
name: 'indexed-job'
spec:
completions: 5
parallelism: 3
completionMode: Indexed
template:
spec:
restartPolicy: Never
initContainers:
- name: 'input'
image: 'docker.io/library/bash'
command:
- "bash"
- "-c"
- |
items=(foo bar baz qux xyz)
echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt
volumeMounts:
- mountPath: /input
name: input
containers:
- name: 'worker'
image: 'docker.io/library/busybox'
command:
- "rev"
- "/input/data.txt"
volumeMounts:
- mountPath: /input
name: input
volumes:
- name: input
emptyDir: {}
위 예제에서, 잡 컨트롤러가 모든 컨테이너에 설정한 JOB_COMPLETION_INDEX
내장 환경 변수를 사용한다. 초기화 컨테이너는
인덱스를 정적 값으로 매핑하고 emptyDir 볼륨을 통해
워커를 실행중인 컨테이너와 공유하는 파일에 쓴다.
선택적으로 컨테이너에 인덱스를 발행하기 위해 downward API를 통해
직접 환경 변수를 정의할
수 있다. 또한 환경변수 또는 파일로 된 컨피그맵(ConfigMap)으로부터
값 목록을 불러올 수도 있다.
혹은 다음 예제와 같이 직접 downward API를 사용하여 어노테이션의 값을 볼륨 파일의 형태로 전달할 수도 있다.
apiVersion: batch/v1
kind: Job
metadata:
name: 'indexed-job'
spec:
completions: 5
parallelism: 3
completionMode: Indexed
template:
spec:
restartPolicy: Never
containers:
- name: 'worker'
image: 'docker.io/library/busybox'
command:
- "rev"
- "/input/data.txt"
volumeMounts:
- mountPath: /input
name: input
volumes:
- name: input
downwardAPI:
items:
- path: "data.txt"
fieldRef:
fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']
이제 잡을 실행하자.
# 첫 번째 접근 방법을 사용한다. ($JOB_COMPLETION_INDEX 에 의존)
kubectl apply -f https://kubernetes.io/examples/application/job/indexed-job.yaml
이 잡을 생성할 때, 컨트롤 플레인은 명시한 각 인덱스당 하나씩 일련의 파드를 생성한다. .spec.parallelism
의 값은 한 번에 실행 가능한 수를 결정하는 반면 .spec.completions
는 잡에서 총 생성되는 파드의 수를 결정한다.
.spec.parallelism
가 .spec.completions
보다 작기 때문에, 컨트롤 플레인은 추가로 파드를 시작하기 전 최초 생성된 파드 중 일부가 완료되기를 기다린다.
아래와 같이 시간 제한(timeout)을 설정하고, 잡이 성공할 때까지 기다린다.
# 조건명은 대소문자를 구분하지 않는다.
kubectl wait --for=condition=complete --timeout=300s job/indexed-job
잡의 상세 설명을 출력하여 성공적으로 수행되었는지 확인한다.
kubectl describe jobs/indexed-job
출력 결과는 다음과 비슷하다.
Name: indexed-job
Namespace: default
Selector: controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
Labels: controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
job-name=indexed-job
Annotations: <none>
Parallelism: 3
Completions: 5
Start Time: Thu, 11 Mar 2021 15:47:34 +0000
Pods Statuses: 2 Running / 3 Succeeded / 0 Failed
Completed Indexes: 0-2
Pod Template:
Labels: controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
job-name=indexed-job
Init Containers:
input:
Image: docker.io/library/bash
Port: <none>
Host Port: <none>
Command:
bash
-c
items=(foo bar baz qux xyz)
echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt
Environment: <none>
Mounts:
/input from input (rw)
Containers:
worker:
Image: docker.io/library/busybox
Port: <none>
Host Port: <none>
Command:
rev
/input/data.txt
Environment: <none>
Mounts:
/input from input (rw)
Volumes:
input:
Type: EmptyDir (a temporary directory that shares a pod's lifetime)
Medium:
SizeLimit: <unset>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 4s job-controller Created pod: indexed-job-njkjj
Normal SuccessfulCreate 4s job-controller Created pod: indexed-job-9kd4h
Normal SuccessfulCreate 4s job-controller Created pod: indexed-job-qjwsz
Normal SuccessfulCreate 1s job-controller Created pod: indexed-job-fdhq5
Normal SuccessfulCreate 1s job-controller Created pod: indexed-job-ncslj
이 예제에서는 각 인덱스에 직접 설정한 값을 갖는 잡을 실행한다. 파드 중 하나의 출력을 검사할 수도 있다.
kubectl logs indexed-job-fdhq5 # 잡에 속한 파드의 이름에 맞춰 변경한다.
출력 결과는 다음과 비슷하다.
xuq
이 예제에서는, 파드의 IP 주소 대신 호스트네임으로 상호 통신할 수 있도록 파드를 생성하는 잡을 색인된 완료 모드(Indexed completion mode)로 구성하여 실행한다.
잡 내부의 파드들이 서로 통신해야 하는 경우가 있다. 각 파드에서 실행되는 사용자 워크로드에서 쿠버네티스 API 서버에 질의하여 다른 파드의 IP를 알아낼 수도 있지만, 쿠버네티스에 내장된 DNS 변환(resolution)을 활용하는 것이 훨씬 간편하다.
색인된 완료 모드의 잡은 자동으로 파드의 호스트네임을 ${jobName}-${completionIndex}
형식으로 설정한다.
이 형식을 활용하면 파드 호스트네임을 결정론적(deterministically)으로 빌드하고 파드 간 통신을 활성화할 수 있다.
쿠버네티스 컨트롤 플레인에 대해 클라이언트 연결을 생성하고 API 요청으로 파드 호스트네임 및 IP를 알아낼 필요 없이 말이다.
이러한 설정은 파드 네트워킹이 필요하지만 쿠버네티스 API 서버와의 네트워크 연결에 의존하지 않고 싶은 경우에 유용하다.
기본적으로 잡 사용 방법에 익숙하다는 것을 가정한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.21. 버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
잡의 파드 호스트네임을 활용한 파드 간 통신을 활성화하기 위해서는 다음 작업을 진행해야 한다.
잡으로 생성되는 파드들에 대한 레이블 셀렉터를 지닌 헤드리스 서비스를 만든다.
헤드리스 서비스는 반드시 잡과 동일한 네임스페이스에 생성해야 한다.
쿠버네티스에 의해 job-name
레이블이 자동으로 생성되므로 job-name: <your-job-name>
셀렉터를 활용하면 간단하다.
해당 설정은 잡에서 실행 중인 파드들의 호스트네임에 대한 레코드를 생성하도록 DNS 시스템을 트리거할 것이다.
잡 템플릿 스펙에 아래 값을 추가함으로써 헤드리스 서비스를 잡의 파드들의 서브도메인 서비스로 설정한다.
subdomain: <headless-svc-name>
아래는 파드 호스트네임을 통해 파드 간 통신이 활성화된 잡의 예시이다. 해당 잡은 모든 파드들이 호스트네임으로 상호 핑(ping)을 성공한 경우에만 완료된다.
apiVersion: v1
kind: Service
metadata:
name: headless-svc
spec:
clusterIP: None # 헤드리스 서비스를 생성하기 위해서는 clusterIP가 반드시 None이어야 한다.
selector:
job-name: example-job # 잡의 name과 일치해야 한다.
---
apiVersion: batch/v1
kind: Job
metadata:
name: example-job
spec:
completions: 3
parallelism: 3
completionMode: Indexed
template:
spec:
subdomain: headless-svc # 서비스의 name과 일치해야 한다.
restartPolicy: Never
containers:
- name: example-workload
image: bash:latest
command:
- bash
- -c
- |
for i in 0 1 2
do
gotStatus="-1"
wantStatus="0"
while [ $gotStatus -ne $wantStatus ]
do
ping -c 1 example-job-${i}.headless-svc > /dev/null 2>&1
gotStatus=$?
if [ $gotStatus -ne $wantStatus ]; then
echo "Failed to ping pod example-job-${i}.headless-svc, retrying in 1 second..."
sleep 1
fi
done
echo "Successfully pinged pod: example-job-${i}.headless-svc"
done
위의 예제를 적용한 이후, <pod-hostname>.<headless-service-name>
를 사용하여 네트워크 상으로 서로 통신해보자.
아래와 유사한 내용이 출력될 것이다.
kubectl logs example-job-0-qws42
Failed to ping pod example-job-0.headless-svc, retrying in 1 second...
Successfully pinged pod: example-job-0.headless-svc
Successfully pinged pod: example-job-1.headless-svc
Successfully pinged pod: example-job-2.headless-svc
<pod-hostname>.<headless-service-name>
이름 형식은
DNS 정책을 None
혹은 Default
로 설정할 경우 제대로 동작하지 않을 것임에 유의해야 한다.
파드 DNS 정책에 대해 더 배우고 싶다면
여기를 참고하자.이 태스크는 공통 템플릿을 기반으로 하는 여러 개의 잡(Job)을 실행하는 것을 보여준다. 이 접근 방식을 사용하여 일괄 작업을 병렬로 처리할 수 있다.
이 예에는 apple, banana 그리고 cherry 세 항목만 있다. 샘플 잡들은 문자열을 출력한 다음 일시 정지하는 각 항목을 처리한다.
이 패턴이 보다 실질적인 유스케이스에 어떻게 부합하는지 알아 보려면 실제 워크로드에서 잡 사용하기를 참고한다.
사용자는 기본적인 내용과, 병렬 작업이 아닌 잡 사용에 대해 익숙해야 한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
기본 템플릿을 사용하려면 커맨드 라인 유틸리티 sed
가 필요하다.
고급 템플릿 예제를 따라하려면, 파이썬(Python)과 파이썬용 Jinja2 템플릿 라이브러리의 설치가 필요하다.
파이썬을 설정했으면, 다음을 실행하여 Jinja2를 설치할 수 있다.
pip install --user jinja2
먼저, 다음의 잡 템플릿을 다운로드해서 job-tmpl.yaml
파일로 저장한다.
다운로드할 내용은 다음과 같다.
apiVersion: batch/v1
kind: Job
metadata:
name: process-item-$ITEM
labels:
jobgroup: jobexample
spec:
template:
metadata:
name: jobexample
labels:
jobgroup: jobexample
spec:
containers:
- name: c
image: busybox:1.28
command: ["sh", "-c", "echo Processing item $ITEM && sleep 5"]
restartPolicy: Never
# job-tmpl.yaml를 다운로드하기 위해 curl을 사용한다
curl -L -s -O https://k8s.io/examples/application/job/job-tmpl.yaml
다운로드한 파일은 아직 유효한 쿠버네티스
매니페스트가 아니다.
대신 해당 템플릿은 사용하기 전에 채워야하는 자리 표시자가 있는 잡 오브젝트의
YAML 표현이다. $ITEM
구문은 쿠버네티스에 의미가 있지 않다.
다음의 셸 스니펫은 sed
를 사용하여 루프 변수로 $ITEM
문자열을 바꾸고,
jobs
라는 임시 디렉터리에 기록한다. 다음과 같이 실행한다.
# 처리할 각 항목에 대해 하나씩, 템플릿을 여러 파일로 확장한다.
mkdir ./jobs
for i in apple banana cherry
do
cat job-tmpl.yaml | sed "s/\$ITEM/$i/" > ./jobs/job-$i.yaml
done
작동하는지 확인한다.
ls jobs/
출력 결과는 다음과 비슷하다.
job-apple.yaml
job-banana.yaml
job-cherry.yaml
모든 유형의 템플릿 언어(예를 들어, Jinja2, ERB)를 사용하거나, 프로그램을 작성하여 잡 매니페스트를 생성할 수 있다.
다음으로, 하나의 kubectl 명령으로 모든 잡을 생성한다.
kubectl create -f ./jobs
출력 결과는 다음과 비슷하다.
job.batch/process-item-apple created
job.batch/process-item-banana created
job.batch/process-item-cherry created
이제, 작업을 확인한다.
kubectl get jobs -l jobgroup=jobexample
출력 결과는 다음과 비슷하다.
NAME COMPLETIONS DURATION AGE
process-item-apple 1/1 14s 22s
process-item-banana 1/1 12s 21s
process-item-cherry 1/1 12s 20s
kubectl 명령에 -l
옵션을 사용하면 이 잡 그룹의
일부인 잡만 선택된다(시스템에서 관련이 없는 다른 잡이 있을 수 있음).
파드도 동일한 레이블 셀렉터를 사용하여 확인할 수 있다.
kubectl get pods -l jobgroup=jobexample
출력 결과는 다음과 비슷하다.
NAME READY STATUS RESTARTS AGE
process-item-apple-kixwv 0/1 Completed 0 4m
process-item-banana-wrsf7 0/1 Completed 0 4m
process-item-cherry-dnfu9 0/1 Completed 0 4m
이 단일 명령을 사용하여 모든 잡의 출력을 한 번에 확인할 수 있다.
kubectl logs -f -l jobgroup=jobexample
출력 결과는 다음과 같아야 한다.
Processing item apple
Processing item banana
Processing item cherry
# 생성한 잡 제거
# 클러스터가 자동으로 잡의 파드들을 정리
kubectl delete job -l jobgroup=jobexample
첫 번째 예제에서, 템플릿의 각 인스턴스는 하나의 파라미터를 가지고, 해당 파라미터는 잡의 이름에도 사용되었다. 그러나, 이름은 특정 문자들만 포함하도록 제한된다.
이런 약간 더 복잡한 예제는 Jinja 템플릿 언어를 사용하여 각 잡에 대한 여러 파라미터로 매니페스트를 생성한 다음 해당 매니페스트에서 오브젝트를 생성한다.
태스크의 이 부분에서는, 한줄 파이썬 스크립트를 사용하여 매니페스트 집합으로 템플릿을 변환한다.
먼저, 다음의 잡 오브젝트 템플릿을 복사하고 붙여넣기하여, job.yaml.jinja2
파일로 저장한다.
{% set params = [{ "name": "apple", "url": "http://dbpedia.org/resource/Apple", },
{ "name": "banana", "url": "http://dbpedia.org/resource/Banana", },
{ "name": "cherry", "url": "http://dbpedia.org/resource/Cherry" }]
%}
{% for p in params %}
{% set name = p["name"] %}
{% set url = p["url"] %}
---
apiVersion: batch/v1
kind: Job
metadata:
name: jobexample-{{ name }}
labels:
jobgroup: jobexample
spec:
template:
metadata:
name: jobexample
labels:
jobgroup: jobexample
spec:
containers:
- name: c
image: busybox:1.28
command: ["sh", "-c", "echo Processing URL {{ url }} && sleep 5"]
restartPolicy: Never
{% endfor %}
위의 템플릿은 파이썬 딕셔너리(dicts)로 구성된 항목(1-4행)을 사용하여 각 잡 오브젝트에 대해
두 개의 파라미터를 정의한다. for
루프는 각 파라미터의 집합(나머지 행)에 대해
하나의 잡 매니페스트를 방출한다.
이 예제는 YAML의 기능에 의존한다. 하나의 YAML 파일은 여러
문서(이 경우, 쿠버네티스 매니페스트)를 포함할 수 있으며, 행에 있는 ---
로
구분된다.
출력 결과를 kubectl
에 직접 파이프를 사용해 잡을 생성할 수 있다.
다음으로, 이 한 줄 파이썬 프로그램을 사용하여 템플릿을 확장한다.
alias render_template='python -c "from jinja2 import Template; import sys; print(Template(sys.stdin.read()).render());"'
render_template
을 사용해서 파라미터와 템플릿을 쿠버네티스 매니페스트가
포함된 하나의 YAML 파일로 변환한다.
# 앞에서 정의한 앨리어스(alias)가 필요하다
cat job.yaml.jinja2 | render_template > jobs.yaml
render_template
스크립트가 제대로 동작하는지 확인하기 위해 jobs.yaml
을
볼 수 있다.
render_template
스크립트가 원하는대로 동작하는 것을 확인했다면,
스크립트의 출력 결과를 파이프를 사용하여 kubectl
에 보낼 수 있다.
cat job.yaml.jinja2 | render_template | kubectl apply -f -
쿠버네티스는 생성한 잡을 수락하고 실행한다.
# 생성한 잡 제거
# 클러스터가 자동으로 잡이 있던 파드를 정리
kubectl delete job -l jobgroup=jobexample
실제 유스케이스에서, 각 잡은 동영상의 프레임을 렌더링하거나, 데이터베이스에서 행 범위를
처리하는 것과 같은 상당한 규모의 계산을 수행한다. 동영상을 렌더링하는 경우 프레임 번호에
$ITEM
을 설정한다. 데이터베이스에서 행을 처리하는
경우, 처리할 데이터베이스 행의 범위를 나타내도록 $ITEM
을 설정한다.
이번 태스크에서, 로그를 가져와 파드에서 출력 결과를 수집하는 명령어를
실행했다. 실제 유스케이스에서, 잡의 각 파드는 완료하기 전에 출력 결과를
내구성있는 스토리지에 기록한다. 각 잡에 대해 퍼시스턴트볼륨(PersistentVolume)을
사용하거나 외장 스토리지 서비스를 사용할 수 있다. 예를 들어, 동영상의 프레임을 렌더링하는 경우,
HTTP를 사용하여 렌더링된 프레임 데이터를 각 프레임에 대한 다른 URL을 사용해서 URL에 PUT
한다.
잡을 생성한 후, 쿠버네티스는 한 잡의 파드를 다른 잡의 파드와 구별하기 위해서 추가 레이블을 자동으로 추가한다.
이 예시에서, 각 잡과 잡의 파드 템플릿은 jobgroup=jobexample
레이블을 갖는다.
쿠버네티스 자체는 jobgroup
이라는 레이블에 신경쓰지 않는다. 템플릿에서
생성한 모든 잡에 대해 레이블을 설정하면 한번에 모든 잡을 편리하게
조작할 수 있다.
첫 번째 예제에서 템플릿을 사용해서
여러 잡을 생성했다. 템플릿은 각 파드도 동일한 레이블을 가질 수 있도록 보장하므로,
단일 명령어로 이러한 템플릿 기반 잡들의 모든 파드에서 확인할 수 있다.
많은 수의 잡 오브젝트의 생성을 계획 중이라면, 아마도 다음의 사항을 파악하게 될 것이다.
아주 많은 잡 오브젝트를 생성하지 않고 많은 양의 작업을 처리하는데 사용할 수 있는 다른 잡 패턴도 있다.
잡 오브젝트를 자동으로 관리하기 위해 자체 컨트롤러를 작성하는 것도 고려할 수 있다.
대시보드는 웹 기반 쿠버네티스 유저 인터페이스이다. 대시보드를 통해 컨테이너화 된 애플리케이션을 쿠버네티스 클러스터에 배포할 수 있고, 컨테이너화 된 애플리케이션을 트러블슈팅할 수 있으며, 클러스터 리소스들을 관리할 수 있다. 대시보드를 통해 클러스터에서 동작 중인 애플리케이션의 정보를 볼 수 있고, 개별적인 쿠버네티스 리소스들을(예를 들면 디플로이먼트, 잡, 데몬셋 등) 생성하거나 수정할 수 있다. 예를 들면, 디플로이먼트를 스케일하거나, 롤링 업데이트를 초기화하거나, 파드를 재시작하거나 또는 배포 마법사를 이용해 새로운 애플리케이션을 배포할 수 있다.
또한 대시보드는 클러스터 내 쿠버네티스 리소스들의 상태와 발생하는 모든 에러 정보를 제공한다.
대시보드 UI는 기본으로 배포되지 않는다. 배포하려면 다음 커맨드를 실행한다.
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.6.1/aio/deploy/recommended.yaml
클러스터 데이터를 보호하기 위해, 대시보드는 기본적으로 최소한의 RBAC 설정을 제공한다. 현재, 대시보드는 Bearer 토큰으로 로그인하는 방법을 제공한다. 본 시연을 위한 토큰을 생성하기 위해서는, 샘플 사용자 만들기 가이드를 따른다.
kubectl
커맨드라인 도구를 이용해 다음 커맨드를 실행함으로써 대시보드로의
접속을 활성화할 수 있다.
kubectl proxy
kubectl은 http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/를 통해 대시보드에 접속할 수 있게 해줄 것이다.
UI는 오직 커맨드가 실행된 머신에서만 접근 가능하다. 상세 내용은 kubectl proxy --help
옵션을 확인한다.
초기 클러스터 대시보드에 접근하면, 환영 페이지를 볼 수 있다.
이 페이지는 첫 애플리케이션을 배포하는 버튼이 있을 뿐만 아니라, 이 문서의 링크를 포함하고 있다.
게다가, 대시보드가 있는 클러스터에서 기본적으로 kube-system
네임스페이스이 동작중인 시스템 애플리케이션을 볼 수 있다.
대시보드를 이용하여 컨테이너화 된 애플리케이션을 디플로이먼트와 간단한 마법사를 통한 선택적인 서비스로 생성하고 배포할 수 있다. 애플리케이션 세부 정보를 수동으로 지정할 수 있고, 또는 애플리케이션 구성을 포함한 YAML 또는 JSON 매니페스트(manifest) 파일을 업로드할 수 있다.
시작하는 페이지의 상위 오른쪽 코너에 있는 CREATE 버튼을 클릭한다.
배포 마법사는 다음 정보를 제공한다.
앱 이름 (필수): 애플리케이션 이름. 레이블 이름은 배포할 모든 디플로이먼트와 서비스에 추가되어야 한다.
애플리케이션 이름은 선택된 쿠버네티스 네임스페이스 안에서 유일해야 한다. 소문자로 시작해야 하며, 소문자 또는 숫자로 끝나고, 소문자, 숫자 및 대쉬(-)만을 포함해야 한다. 24 문자만을 제한한다. 처음과 끝의 스페이스는 무시된다.
컨테이너 이미지 (필수): 레지스트리에 올라간 퍼블릭 도커 컨테이너 이미지 또는 프라이빗 이미지(대체로 Google Container Registry 또는 도커 허브에 올라간)의 URL. 컨테이너 이미지 사양은 콜론으로 끝난다.
파드의 수 (필수): 배포하고 싶은 애플리케이션의 원하는 목표 파드 개수. 값은 양의 정수만 허용됩니다.
클러스터에 의도한 파드의 수를 유지하기 위해서 디플로이먼트가 생성될 것이다.
서비스 (선택): 일부 애플리케이션의 경우, (예를 들어, 프론트엔드) 아마도 클러스터 바깥의 퍼블릭 IP 주소를 가진 (외부 서비스) 외부에 서비스를 노출시키고 싶을 수 있다.
클러스터 내부에서만 보고 싶은 어떤 서비스들이 있을 것이다. 이를 내부 서비스라고 한다.
서비스 타입과는 무관하게, 서비스 생성을 선택해서 컨테이너의 (들어오는 패킷의) 포트를 리슨한다면, 두 개의 포트를 정의해야 한다. 서비스는 컨테이너가 바라보는 타겟 포트와 (들어오는 패킷의) 맵핑하는 포트가 만들어져야 할 것이다. 서비스는 배포된 파드에 라우팅 될 것이다. 지원하는 프로토콜은 TCP와 UDP이다. 서비스가 이용하는 내부 DNS 이름은 애플리케이션 이름으로 지정한 값이 될 것이다.
만약 필요하다면, 더 많은 세팅을 지정할 수 있는 자세한 옵션 보기 섹션에서 확장할 수 있다.
설명: 입력하는 텍스트값은 디플로이먼트에 어노테이션으로 추가될 것이고, 애플리케이션의 세부사항에 표시될 것이다.
레이블: 애플리케이션에 사용되는 기본적인 레이블은 애플리케이션 이름과 버전이다. 릴리스, 환경, 티어, 파티션, 그리고 릴리스 트랙과 같은 레이블을 디플로이먼트, 서비스, 그리고 파드를 생성할 때 추가적으로 정의할 수 있다.
예를 들면:
release=1.0
tier=frontend
environment=pod
track=stable
네임스페이스: 쿠버네티스는 동일한 물리 클러스터를 바탕으로 여러 가상의 클러스터를 제공한다. 이러한 가상 클러스터들을 네임스페이스라고 부른다. 논리적으로 명명된 그룹으로 리소스들을 분할할 수 있다.
대시보드는 드롭다운 리스트로 가능한 모든 네임스페이스를 제공하고, 새로운 네임스페이스를 생성할 수 있도록 한다. 네임스페이스 이름은 최대 63개의 영숫자 단어와 대시(-)를 포함하고 있지만 대문자를 가지지 못한다. 네임스페이스 이름은 숫자로만 구성할 수 없다. 만약 이름을 10이라는 숫자로 세팅한다면, 파드는 기본 네임스페이스로 배정하게 될 것이다.
네임스페이스 생성이 성공하는 경우, 생성된 네임스페이스가 기본으로 선택된다. 만약 생성에 실패하면, 첫 번째 네임스페이스가 선택된다.
이미지 풀(Pull) 시크릿: 특정 도커 컨테이너 이미지가 프라이빗한 경우, 풀(Pull) 시크릿 자격 증명을 요구한다.
대시보드는 가능한 모든 시크릿을 드롭다운 리스트로 제공하며, 새로운 시크릿을 생성할 수 있도록 한다.
시크릿 이름은 예를 들어 new.image-pull.secret
과 같이 DNS 도메인 이름 구문으로 따르기로 한다.
시크릿 내용은 base64 인코딩 방식이며,
.dockercfg
파일로 정의된다.
시크릿 이름은 최대 253 문자를 포함할 수 있다.
이미지 풀(Pull) 시크릿의 생성이 성공한 경우, 기본으로 선택된다. 만약 생성에 실패하면, 시크릿은 허용되지 않는다.
CPU 요구 사항 (cores) 와 메모리 요구 사항 (MiB): 컨테이너를 위한 최소 리소스 상한을 정의할 수 있다. 기본적으로, 파드는 CPU와 메모리 상한을 두지 않고 동작한다.
커맨드 실행 와 커맨드 인수 실행: 기본적으로, 컨테이너는 선택된 도커 이미지의 기본 엔트리포인트 커맨드를 실행한다. 커맨드 옵션과 인자를 기본 옵션에 우선 적용하여 사용할 수 있다.
특권을 가진(privileged) 상태로 실행: 다음 세팅은 호스트에서 루트 권한을 가진 프로세스들이 특권을 가진 컨테이너의 프로세스들과 동등한지 아닌지 정의한다. 특권을 가진(privileged) 컨테이너는 네트워크 스택과 디바이스에 접근하는 것을 조작하도록 활용할 수 있다.
환경 변수: 쿠버네티스 서비스를
환경 변수를 통해 노출한다.
환경 변수 또는 인자를 환경 변수들의 값으로 커맨드를 통해 구성할 수 있다.
애플리케이션들이 서비스를 찾는데 사용된다.
값들은 $(VAR_NAME)
구문을 사용하는 다른 변수들로 참조할 수 있다.
쿠버네티스는 선언적인 설정을 제공한다. 이 방식에서는 모든 설정이 매니페스트(YAML 또는 JSON 설정 파일)에 저장된다. 매니페스트는 쿠버네티스 API 리소스 스키마를 사용한다.
배포 마법사를 통해 애플리케이션 세부 사항들을 지정하는 대신, 애플리케이션을 하나 이상의 매니페스트로 정의할 수 있고 대시보드를 이용해서 파일을 업로드할 수 있다.
다음 섹션들은 어떻게 제공하고 어떻게 사용할 수 있는지에 대한 쿠버네티스 대시보드 UI의 모습을 보여준다.
클러스터에 정의된 쿠버네티스 오프젝트가 있으면, 대시보드는 초기화된 뷰를 제공한다. 기본적으로 기본 네임스페이스의 오프젝트만이 보이는데, 이는 탐색 창에 위치한 네임스페이스 셀렉터를 이용해 변경할 수 있다.
대시보드는 몇 가지 메뉴 카테고리 중에서 대부분의 쿠버네티스 오브젝트 종류와 그룹을 보여준다.
클러스터와 네임스페이스 관리자에게 대시보드는 노드, 네임스페이스 그리고 퍼시스턴트 볼륨과 세부사항들이 보여진다. 노드는 모든 노드를 통틀어 CPU와 메모리 사용량을 보여준다. 세부사항은 각 노드들에 대한 사용량, 사양, 상태, 할당된 리소스, 이벤트 그리고 노드에서 돌아가는 파드를 보여준다.
선택된 네임스페이스에서 구동되는 모든 애플리케이션을 보여준다. 해당 뷰는 애플리케이션의 워크로드 종류(예시: 디플로이먼트, 레플리카셋(ReplicaSet), 스테이트풀셋(StatefulSet))를 보여준다. 각각의 워크로드 종류는 분리하여 볼 수 있다. 리스트는 예를 들어 레플리카셋에서 준비된 파드의 숫자 또는 파드의 현재 메모리 사용량과 같은 워크로드에 대한 실용적인 정보를 요약한다.
워크로드에 대한 세부적인 것들은 상태와 사양 정보, 오프젝트들 간의 관계를 보여준다. 예를 들어, 레플리카셋으로 관리하는 파드들 또는 새로운 레플리카셋과 디플로이먼트를 위한 Horizontal Pod Autoscalers 이다.
외부로 노출되는 서비스들과 클러스터 내에 발견되는 서비스들을 허용하는 쿠버네티스 리소스들을 보여준다. 이러한 이유로 서비스와 인그레스는 클러스터간의 연결을 위한 내부 엔드포인트들과 외부 사용자를 위한 외부 엔드포인트들에 의해 타게팅된 파드들을 보여준다.
스토리지는 애플리케이션이 데이터를 저장하기 위해 사용하는 퍼시턴트볼륨클레임 리소스들을 보여준다.
클러스터에서 동작 중인 애플리케이션의 라이브 설정을 사용하는 모든 쿠버네티스 리소스들을 보여준다. 컨피그 오브젝트들을 수정하고 관리할 수 있도록 허용하며, 기본적으로는 숨겨져 있는 시크릿들을 보여준다.
파드 목록과 세부사항 페이지들은 대시보드에 구현된 로그 뷰어에 링크된다. 뷰어는 단일 파드에 있는 컨테이너들의 로그들을 내려가면 볼 수 있도록 한다.
더 많은 정보는 쿠버네티스 대시보드 프로젝트 페이지를 참고한다.
여기에서는 클러스터와 통신을 하는 다양한 방식에 대해서 다룰 것이다.
최초로 쿠버네티스 API에 접근할 때 우리는
쿠버네티스 CLI인 kubectl
을 사용하는 것을 추천한다.
클러스터에 접근하려면 클러스터의 위치정보를 알아야 하고 클러스터에 접속하기 위한 인증정보를 가져야 한다. 일반적으로 이는 당신이 Getting started guide를 다 진행했을 때 자동으로 구성되거나, 다른 사람이 클러스터를 구성하고 당신에게 인증정보와 위치정보를 제공할 수도 있다.
kubectl이 인지하는 위치정보와 인증정보는 다음 커맨드로 확인한다.
kubectl config view
여기에서
kubectl
사용 예시를 볼 수 있으며, 완전한 문서는
kubectl 레퍼런스에서 확인할 수 있다.
kubectl은 apiserver의 위치 파악과 인증을 처리한다. 만약 당신이 curl, wget 또는 웹브라우저와 같은 http 클라이언트로 REST API에 직접 접근하려고 한다면 위치 파악과 인증을 하는 몇 가지 방법이 존재한다.
다음 커맨드는 kubectl을 리버스 프록시(reverse proxy)처럼 동작하는 모드를 실행한다. 이는 apiserver의 위치지정과 인증을 처리한다. 다음과 같이 실행한다.
kubectl proxy --port=8080
상세 내용은 kubectl proxy를 참조한다
이후에 당신은 curl, wget, 웹브라우저로 다음과 같이 API를 탐색할 수 있다. localhost는 IPv6 주소 [::1]로도 대체할 수 있다.
curl http://localhost:8080/api/
결괏값은 다음과 같을 것이다.
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "10.0.1.149:443"
}
]
}
kubectl apply
및 kubectl describe secret...
명령과 grep/cut을 활용하여 기본 서비스 어카운트의 토큰을 생성한다.
먼저, 기본 서비스어카운트를 위한 토큰을 요청하는 시크릿을 생성한다.
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: default-token
annotations:
kubernetes.io/service-account.name: default
type: kubernetes.io/service-account-token
EOF
다음으로, 토큰 컨트롤러가 해당 시크릿에 토큰을 채우기를 기다린다.
while ! kubectl describe secret default-token | grep -E '^token' >/dev/null; do
echo "waiting for token..." >&2
sleep 1
done
결과를 캡처하여 생성된 토큰을 사용한다.
APISERVER=$(kubectl config view --minify | grep server | cut -f 2- -d ":" | tr -d " ")
TOKEN=$(kubectl describe secret default-token | grep -E '^token' | cut -f2 -d':' | tr -d " ")
curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure
결과값은 다음과 같을 것이다.
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "10.0.1.149:443"
}
]
}
jsonpath
를 사용한다면 다음과 같다.
APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
TOKEN=$(kubectl get secret default-token -o jsonpath='{.data.token}' | base64 --decode)
curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure
결과값은 다음과 같을 것이다.
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "10.0.1.149:443"
}
]
}
위 예제에서는 --insecure
flag를 사용했다. 이는 MITM 공격을 받을 수 있는 상태로
두는 것이다. kubectl로 클러스터에 접속할 때 저장된 root 인증서와 클라이언트 인증서들을
서버 접속에 사용한다.
(이들은 ~/.kube
디렉터리에 설치된다.)
일반적으로 self-signed 인증서가 클러스터 인증서로 사용되므로 당신의 http 클라이언트가
root 인증서를 사용하려면 특수한 설정을 필요로 할 것이다.
localhost에서 제공되거나 방화벽으로 보호되는 몇몇 클러스터들에서는 apiserver가 인증을 요구하지 않지만 이는 표준이 아니다. API에 대한 접근 제어은 클러스터 관리자가 이를 어떻게 구성할 수 있는지를 설명한다.
쿠버네티스는 공식적으로 Go와 Python 클라이언트 라이브러리를 지원한다.
go get k8s.io/client-go@kubernetes-<kubernetes-version-number>
커맨드를 실행한다. INSTALL.md에서 상세한 설치 방법을 알 수 있다. https://github.com/kubernetes/client-go에서 어떤 버젼이 지원되는지 확인할 수 있다.import "k8s.io/client-go/kubernetes"
로 import하는 것을 예로 들 수 있다.Go 클라이언트는 apiserver의 위치지정과 인증에 kubectl CLI와 동일하게 kubeconfig file을 사용할 수 있다. 예제를 참고한다.
만약 애플리케이션이 클러스터 내에 파드로 배포되었다면 다음 장을 참조하기를 바란다.
Python 클라이언트를 사용하려면 pip install kubernetes
커맨드를 실행한다. 설치 옵션에 대한 상세 사항은 Python Client Library page를 참조한다.
Python 클라이언트는 apiserver의 위치지정과 인증에 kubectl CLI와 동일하게 kubeconfig file을 사용할 수 있다. 예제를 참조한다.
다른 언어에서 API를 접속하기 위한 클라이언트 라이브러리들도 존재한다. 이들이 어떻게 인증하는지는 다른 라이브러리들의 문서를 참조한다.
파드에서 API에 접근하는 경우, API 서버를 찾고 인증하는 방식이 약간 다를 수 있다.
더 자세한 내용은 파드 내에서 쿠버네티스 API에 접근을 참조한다.
이전 섹션에서는 쿠버네티스 API 서버에 연결하는 방법을 소개하였다. 쿠버네티스 클러스터에서 실행되는 다른 서비스에 연결하는 방법은 클러스터 서비스에 접근 페이지를 참조한다.
redirect 기능은 deprecated되고 제거 되었다. 대신 (아래의) 프록시를 사용하기를 바란다.
쿠버네티스를 사용하면서 당신이 접할 수 있는 몇 가지 다른 프록시들이 존재한다.
apiserver(s) 전면의 Proxy/Load-balancer:
외부 서비스의 Cloud Load Balancer들:
LoadBalancer
라면 자동으로 생성된다일반적으로 쿠버네티스 사용자들은 처음 두 타입이 아닌 다른 방식은 고려할 필요가 없지만 클러스터 관리자는 나머지 타입을 적절하게 구성해줘야 한다.
이 페이지에서는 구성 파일을 사용하여 다수의 클러스터에 접근할 수 있도록
설정하는 방식을 보여준다. 클러스터, 사용자, 컨텍스트가 하나 이상의
구성 파일에 정의된 다음 kubectl config use-context
커맨드를
사용하여 클러스터를 빠르게 변경할 수 있다.
kubeconfig
라는 이름을 가진 파일이
반드시 존재해야 한다는 것을 의미하는 것은 아니다.쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
kubectl이 설치되었는지 확인하려면,
kubectl version --client
을 실행한다. kubectl 버전은 클러스터의 API 서버 버전과
마이너 버전 하나 차이 이내여야
한다.
당신이 개발 작업을 위한 클러스터와 테스트 작업을 위한 클러스터를 가지고 있다고 가정해보자.
development
클러스터에서는 프런트 엔드 개발자들이 frontend
라는 네임스페이스에서
작업을 하고 있고, 스토리지 개발자들은 storage
라는 네임스페이스에서 작업을 하고 있다.
test
클러스터에서는 개발자들이 default 네임스페이스에서 개발하거나 필요에 따라 보조
네임스페이스들을 생성하고 있다. development 클러스터에 접근하려면 인증서로 인증을 해야 하고,
test 클러스터에 접근하려면 사용자네임과 패스워드로 인증을 해야 한다.
config-exercise
라는 디렉터리를 생성한다. config-exercise
디렉터리에
다음 내용을 가진 config-demo
라는 파일을 생성한다.
apiVersion: v1
kind: Config
preferences: {}
clusters:
- cluster:
name: development
- cluster:
name: test
users:
- name: developer
- name: experimenter
contexts:
- context:
name: dev-frontend
- context:
name: dev-storage
- context:
name: exp-test
구성 파일은 클러스터들, 사용자들, 컨텍스트들을 기술한다. config-demo
파일은 두 클러스터들과
두 사용자들, 세 컨텍스트들을 기술하기 위한 프레임워크를 가진다.
config-exercise
디렉터리로 이동한다. 그리고 다음 커맨드들을 실행하여 구성 파일에 클러스터의
세부사항들을 추가한다.
kubectl config --kubeconfig=config-demo set-cluster development --server=https://1.2.3.4 --certificate-authority=fake-ca-file
kubectl config --kubeconfig=config-demo set-cluster test --server=https://5.6.7.8 --insecure-skip-tls-verify
사용자의 세부사항들을 구성 파일에 추가한다.
kubectl config --kubeconfig=config-demo set-credentials developer --client-certificate=fake-cert-file --client-key=fake-key-seefile
kubectl config --kubeconfig=config-demo set-credentials experimenter --username=exp --password=some-password
kubectl --kubeconfig=config-demo config unset users.<name>
를 실행한다.kubectl --kubeconfig=config-demo config unset clusters.<name>
를 실행한다.kubectl --kubeconfig=config-demo config unset contexts.<name>
를 실행한다.컨텍스트 세부사항들을 구성 파일에 추가한다.
kubectl config --kubeconfig=config-demo set-context dev-frontend --cluster=development --namespace=frontend --user=developer
kubectl config --kubeconfig=config-demo set-context dev-storage --cluster=development --namespace=storage --user=developer
kubectl config --kubeconfig=config-demo set-context exp-test --cluster=test --namespace=default --user=experimenter
config-demo
파일을 열어서 세부사항들이 추가되었는지 확인한다. config-demo
파일을 열어보는
것 대신에 config view
커맨드를 사용할 수도 있다.
kubectl config --kubeconfig=config-demo view
두 클러스터, 두 사용자, 세 컨텍스트들이 출력 결과로 나온다.
apiVersion: v1
clusters:
- cluster:
certificate-authority: fake-ca-file
server: https://1.2.3.4
name: development
- cluster:
insecure-skip-tls-verify: true
server: https://5.6.7.8
name: test
contexts:
- context:
cluster: development
namespace: frontend
user: developer
name: dev-frontend
- context:
cluster: development
namespace: storage
user: developer
name: dev-storage
- context:
cluster: test
namespace: default
user: experimenter
name: exp-test
current-context: ""
kind: Config
preferences: {}
users:
- name: developer
user:
client-certificate: fake-cert-file
client-key: fake-key-file
- name: experimenter
user:
# 문서 참고 사항 (이 설명은 명령 출력의 일부가 아니다.)
# 쿠버네티스 클라이언트 구성에 암호를 저장하는 것은 위험하다.
# 자격 증명 플러그인을 사용하여
# 자격 증명을 별도로 저장하는 것이 더 나은 대안이다.
# 다음을 참고하자. https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins
password: some-password
username: exp
위 fake-ca-file
, fake-cert-file
, fake-key-file
은 인증서 파일들의 실제 경로 이름을 위한
플레이스홀더(placeholder)이다.
당신의 환경에 맞게 이들을 실제 인증서 경로로 변경해줘야 한다.
만약 당신이 인증서 파일들의 경로 대신에 여기에 포함된 base64로 인코딩된 데이터를 사용하려고 한다면
이 경우 키에 -data
접미사를 추가해야 한다. 예를 들면 certificate-authority-data
,
client-certificate-data
, client-key-data
같이 사용할 수 있다.
컨텍스트는 세 가지(클러스터, 사용자, 네임스페이스) 요소들로 이뤄진다. 예를 들어
dev-frontend
컨텍스트는 "development
클러스터의 frontend
네임스페이스에 접근하는데
developer
사용자 자격증명을 사용하라고 알려준다."
현재 컨텍스트를 설정한다.
kubectl config --kubeconfig=config-demo use-context dev-frontend
이제 당신이 kubectl
커맨드를 입력할 때마다 dev-frontend
컨텍스트에 명시된 클러스터와
네임스페이스 상에서 동작하게 될 것이다. 그리고 커맨드는 dev-frontend
컨텍스트 내에 명시된
사용자 자격증명을 사용할 것이다.
현재 컨텍스트에 관련된 구성 정보만을 보려면
--minify
플래그를 사용한다.
kubectl config --kubeconfig=config-demo view --minify
dev-frontend
컨텍스트에 관련된 구성 정보가 출력 결과로 표시될 것이다.
apiVersion: v1
clusters:
- cluster:
certificate-authority: fake-ca-file
server: https://1.2.3.4
name: development
contexts:
- context:
cluster: development
namespace: frontend
user: developer
name: dev-frontend
current-context: dev-frontend
kind: Config
preferences: {}
users:
- name: developer
user:
client-certificate: fake-cert-file
client-key: fake-key-file
이제 당신이 잠시 test 클러스터에서 작업하려고 한다고 가정해보자.
현재 컨텍스트를 exp-test
로 변경한다.
kubectl config --kubeconfig=config-demo use-context exp-test
이제 당신이 실행하는 모든 kubectl
커맨드는 test
클러스터의
default 네임스페이스에 적용되며 exp-test
컨텍스트에 나열된
사용자의 자격증명을 사용할 것이다.
현재의 컨텍스트인 exp-test
에 관련된 설정을 보자.
kubectl config --kubeconfig=config-demo view --minify
마지막으로 당신이 development
클러스터의 storage
네임스페이스에서
잠시 작업을 하려고 한다고 가정해보자.
현재 컨텍스트를 dev-storage
로 변경한다.
kubectl config --kubeconfig=config-demo use-context dev-storage
현재 컨텍스트인 dev-storage
에 관련된 설정을 보자.
kubectl config --kubeconfig=config-demo view --minify
config-exercise
디렉터리에서 다음 내용으로 config-demo-2
라는 파일을 생성한다.
apiVersion: v1
kind: Config
preferences: {}
contexts:
- context:
cluster: development
namespace: ramp
user: developer
name: dev-ramp-up
위 구성 파일은 dev-ramp-up
이라는 신규 컨텍스트를 정의한다.
KUBECONFIG
라는 환경 변수를 가지고 있는지 확인해보자. 만약 가지고 있다면,
이후에 복원할 수 있도록 KUBECONFIG
환경 변수의 현재 값을 저장한다.
예:
export KUBECONFIG_SAVED="$KUBECONFIG"
$Env:KUBECONFIG_SAVED=$ENV:KUBECONFIG
KUBECONFIG
환경 변수는 구성 파일들의 경로의 리스트이다. 이 리스트는
리눅스와 Mac에서는 콜론으로 구분되며 윈도우에서는 세미콜론으로 구분된다.
KUBECONFIG
환경 변수를 가지고 있다면, 리스트에 포함된 구성 파일들에
익숙해지길 바란다.
다음 예와 같이 임시로 KUBECONFIG
환경 변수에 두 개의 경로들을 덧붙여보자.
export KUBECONFIG="${KUBECONFIG}:config-demo:config-demo-2"
$Env:KUBECONFIG=("config-demo;config-demo-2")
config-exercise
디렉터리에서 다음 커맨드를 입력한다.
kubectl config view
당신의 KUBECONFIG
환경 변수에 나열된 모든 파일들이 합쳐진 정보가 출력 결과로
표시될 것이다. 특히, 합쳐진 정보가 config-demo-2
파일의 dev-ramp-up
컨텍스트와 config-demo
파일의 세 개의 컨텍스트들을
가지고 있다는 것에 주목하길 바란다.
contexts:
- context:
cluster: development
namespace: frontend
user: developer
name: dev-frontend
- context:
cluster: development
namespace: ramp
user: developer
name: dev-ramp-up
- context:
cluster: development
namespace: storage
user: developer
name: dev-storage
- context:
cluster: test
namespace: default
user: experimenter
name: exp-test
kubeconfig 파일들을 어떻게 병합하는지에 대한 상세정보는 kubeconfig 파일을 사용하여 클러스터 접근 구성하기를 참조한다.
만약 당신이 이미 클러스터를 가지고 있고 kubectl
을 사용하여
해당 클러스터를 제어하고 있다면, 아마 $HOME/.kube
디렉터리에 config
라는
파일을 가지고 있을 것이다.
$HOME/.kube
로 가서 어떤 파일들이 존재하는지 보자.
보통 config
라는 파일이 존재할 것이다. 해당 디렉터리 내에는 다른 구성 파일들도 있을 수 있다.
간단하게 말하자면 당신은 이 파일들의 컨텐츠에 익숙해져야 한다.
당신이 $HOME/.kube/config
파일을 가지고 있는데 KUBECONFIG
환경 변수에 나타나지 않는다면 KUBECONFIG
환경 변수에 추가해보자.
예:
export KUBECONFIG="${KUBECONFIG}:${HOME}/.kube/config"
$Env:KUBECONFIG="$Env:KUBECONFIG;$HOME\.kube\config"
이제 KUBECONFIG
환경 변수에 리스트에 포함된 모든 파일들이 합쳐진 구성 정보를 보자.
config-exercise 디렉터리에서 다음 커맨드를 실행한다.
kubectl config view
KUBECONFIG
환경 변수를 원래 값으로 되돌려 놓자. 예를 들면:
export KUBECONFIG="$KUBECONFIG_SAVED"
$Env:KUBECONFIG=$ENV:KUBECONFIG_SAVED
클러스터 인증 후 어떤 속성(사용자 이름, 그룹)을 얻을 수 있는지 항상 명확하지는 않다. 동시에 두 개 이상의 클러스터를 관리하는 경우 훨씬 더 어려울 수 있다.
선택되어 있는 쿠버네티스 컨텍스트의 사용자 이름 등에 대한,
주체 속성을 확인하기 위한 'kubectl' 알파 하위 명령 kubectl alpha auth whoami
이 있다.
더 자세한 내용은 클라이언트의 인증 정보에 대한 API 액세스 를 확인한다.
이 페이지는 kubectl port-forward
를 사용해서 쿠버네티스 클러스터 내에서
실행중인 MongoDB 서버에 연결하는 방법을 보여준다. 이 유형의 연결은 데이터베이스
디버깅에 유용할 수 있다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.10. 버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
MongoDB Shell을 설치한다.
MongoDB를 실행하기 위해 디플로이먼트를 생성한다.
kubectl apply -f https://k8s.io/examples/application/mongodb/mongo-deployment.yaml
성공적인 명령어의 출력은 디플로이먼트가 생성됐다는 것을 확인해준다.
deployment.apps/mongo created
파드 상태를 조회하여 파드가 준비되었는지 확인한다.
kubectl get pods
출력은 파드가 생성되었다는 것을 보여준다.
NAME READY STATUS RESTARTS AGE
mongo-75f59d57f4-4nd6q 1/1 Running 0 2m4s
디플로이먼트 상태를 조회한다.
```shell
kubectl get deployment
출력은 디플로이먼트가 생성되었다는 것을 보여준다.
NAME READY UP-TO-DATE AVAILABLE AGE
mongo 1/1 1 1 2m21s
디플로이먼트는 자동으로 레플리카셋을 관리한다. 아래의 명령어를 사용하여 레플리카셋 상태를 조회한다.
kubectl get replicaset
출력은 레플리카셋이 생성되었다는 것을 보여준다.
NAME DESIRED CURRENT READY AGE
mongo-75f59d57f4 1 1 1 3m12s
MongoDB를 네트워크에 노출시키기 위해 서비스를 생성한다.
kubectl apply -f https://k8s.io/examples/application/mongodb/mongo-service.yaml
성공적인 커맨드의 출력은 서비스가 생성되었다는 것을 확인해준다.
service/mongo created
서비스가 생성되었는지 확인한다.
kubectl get service mongo
출력은 서비스가 생성되었다는 것을 보여준다.
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mongo ClusterIP 10.96.41.183
3. MongoDB 서버가 파드 안에서 실행되고 있고, 27017번 포트에서 수신하고 있는지 확인한다.
```shell
# mongo-75f59d57f4-4nd6q 를 당신의 파드 이름으로 대체한다.
kubectl get pod mongo-75f59d57f4-4nd6q --template='{{(index (index .spec.containers 0).ports 0).containerPort}}{{"\n"}}'
출력은 파드 내 MongoDB 포트 번호를 보여준다.
27017
(27017은 인터넷 상의 MongoDB에 할당된 TCP 포트이다.)
kubectl port-forward
명령어는 파드 이름과 같이 리소스 이름을 사용하여 일치하는 파드를 선택해 포트 포워딩하는 것을 허용한다.
# mongo-75f59d57f4-4nd6q 를 당신의 파드 이름으로 대체한다.
kubectl port-forward mongo-75f59d57f4-4nd6q 28015:27017
이것은
kubectl port-forward pods/mongo-75f59d57f4-4nd6q 28015:27017
또는
kubectl port-forward deployment/mongo 28015:27017
또는
kubectl port-forward replicaset/mongo-75f59d57f4 28015:27017
또는 다음과 같다.
kubectl port-forward service/mongo 28015:27017
위의 명령어들은 모두 동일하게 동작한다. 이와 유사하게 출력된다.
Forwarding from 127.0.0.1:28015 -> 27017
Forwarding from [::1]:28015 -> 27017
kubectl port-forward
는 프롬프트를 리턴하지 않으므로, 이 연습을 계속하려면 다른 터미널을 열어야 한다.MongoDB 커맨드라인 인터페이스를 실행한다.
mongosh --port 28015
MongoDB 커맨드라인 프롬프트에 ping
명령을 입력한다.
db.runCommand( { ping: 1 } )
성공적인 핑 요청을 반환한다.
{ ok: 1 }
만약 특정 로컬 포트가 필요하지 않다면, kubectl
이 로컬 포트를 선택 및 할당하게 하여,
조금 더 단순한 문법으로 로컬 포트 충돌 관리를 위한
부담을 줄일 수 있다.
kubectl port-forward deployment/mongo :27017
kubectl
도구는 사용 중이 아닌 로컬 포트 번호를 찾는다 (낮은 포트 번호는
다른 애플리케이션에서 사용될 것이므로, 낮은 포트 번호를 피해서). 출력은 다음과 같을 것이다.
Forwarding from 127.0.0.1:63753 -> 27017
Forwarding from [::1]:63753 -> 27017
로컬 28015 포트에 대한 연결은 MongoDB 서버가 실행중인 파드의 27017 포트로 포워딩된다. 이 연결로 로컬 워크스테이션에서 파드 안에서 실행 중인 데이터베이스를 디버깅하는데 사용할 수 있다.
kubectl port-forward에 대해 더 알아본다.
이 문서는 외부 클라이언트가 클러스터에서 실행 중인 애플리케이션에 접근하기 위해 사용하는 쿠버네티스 서비스 오브젝트를 생성하는 방법을 설명한다. 서비스는 실행 중인 두 개의 인스턴스를 갖는 애플리케이션에 대한 로드 밸런싱을 제공한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
다음은 애플리케이션 디플로이먼트(Deployment) 설정 파일이다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world
spec:
selector:
matchLabels:
run: load-balancer-example
replicas: 2
template:
metadata:
labels:
run: load-balancer-example
spec:
containers:
- name: hello-world
image: gcr.io/google-samples/node-hello:1.0
ports:
- containerPort: 8080
protocol: TCP
클러스터 내 Hello World 애플리케이션을 실행하자. 위 파일을 사용하여 애플리케이션 디플로이먼트를 생성하자.
kubectl apply -f https://k8s.io/examples/service/access/hello-application.yaml
앞의 명령은 디플로이먼트 오브젝트와 연관된 레플리카셋(ReplicaSet) 오브젝트를 생성한다. 레플리카셋은 두 개의 파드를 갖고, 각각은 Hello World 애플리케이션을 실행한다.
디플로이먼트에 대한 정보를 보여준다.
kubectl get deployments hello-world
kubectl describe deployments hello-world
레플리카셋 오브젝트에 대한 정보를 보여준다.
kubectl get replicasets
kubectl describe replicasets
디플로이먼트를 노출하는 서비스 오브젝트를 생성한다.
kubectl expose deployment hello-world --type=NodePort --name=example-service
서비스에 대한 정보를 보여준다.
kubectl describe services example-service
결과는 아래와 같다.
Name: example-service
Namespace: default
Labels: run=load-balancer-example
Annotations: <none>
Selector: run=load-balancer-example
Type: NodePort
IP: 10.32.0.16
Port: <unset> 8080/TCP
TargetPort: 8080/TCP
NodePort: <unset> 31496/TCP
Endpoints: 10.200.1.4:8080,10.200.2.5:8080
Session Affinity: None
Events: <none>
서비스의 노드포트(NodePort) 값을 메모하자. 예를 들어, 앞선 결과에서, 노드포트 값은 31496이다.
Hello World 애플리케이션이 실행 중인 파드를 나열한다.
kubectl get pods --selector="run=load-balancer-example" --output=wide
결과는 아래와 같다.
NAME READY STATUS ... IP NODE
hello-world-2895499144-bsbk5 1/1 Running ... 10.200.1.4 worker1
hello-world-2895499144-m1pwt 1/1 Running ... 10.200.2.5 worker2
Hello World 파드가 실행 중인 노드들 중 하나의 노드에 대해 공용
IP 주소를 얻자. 이 주소를 얻는 방법은 어떻게 클러스터를 설치했는지에
따라 다르다. 예를 들어, Minikube를 사용하면, kubectl cluster-info
를
실행하여 노드 주소를 알 수 있다. Google Compute Engine 인스턴스를
사용하면, gcloud compute instances list
명령어를
사용하여 노드들의 공용 주소를 알 수
있다.
선택한 노드에서 노드 포트에 대해 TCP 통신을 허용하도록 방화벽 규칙을 생성하자. 예를 들어, 서비스의 노드포트 값이 31568인 경우, 31568 포트로 TCP 통신을 허용하도록 방화벽 규칙을 생성하자. 다른 클라우드 공급자는 방화벽 규칙을 설정하는 다른 방법을 제공한다.
Hello World 애플리케이션 접근을 위해 노드 주소와 노드 포트를 사용하자.
curl http://<public-node-ip>:<node-port>
<public-node-ip>
는 노드의 공용 IP 주소이고,
<node-port>
는 서비스의 노드포트 값이다.
성공적인 요청에 대한 응답은 hello 메시지이다.
Hello Kubernetes!
kubectl expose
를 사용하는 대신,
서비스 설정 파일을 사용해
서비스를 생성할 수 있다.
서비스를 삭제하기 위해 다음 명령어를 입력하자.
kubectl delete services example-service
디플로이먼트, 레플리카셋, Hello World 애플리케이션이 실행 중인 파드를 삭제하기 위해 다음 명령어를 입력하자.
kubectl delete deployment hello-world
튜토리얼 서비스와 애플리케이션 연결하기 따라하기
이 작업은 프론트엔드 와 백엔드 마이크로서비스를 어떻게 생성하는지를 설명한다. 백엔드 마이크로서비스는 인사하기(hello greeter)이다. 프론트엔드는 nginx 및 쿠버네티스 서비스 오브젝트를 사용해 백엔드를 노출한다.
hello
백엔드 마이크로서비스를 생성하고 실행한다.nginx
프론트엔드 마이크로서비스를 생성하고 실행한다.type=LoadBalancer
의 서비스 오브젝트를 사용해 클러스터 외부에 프론트엔드 마이크로서비스를
노출한다.쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
이 작업은 지원되는 환경이 필요한 외부 로드밸런서가 있는 서비스를 사용한다. 만약, 이를 지원하지 않는 환경이라면, 노드포트 서비스 타입을 대신 사용할 수 있다.
백엔드는 인사하기라는 간단한 마이크로서비스이다. 여기에 백엔드 디플로이먼트 구성 파일이 있다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
spec:
selector:
matchLabels:
app: hello
tier: backend
track: stable
replicas: 3
template:
metadata:
labels:
app: hello
tier: backend
track: stable
spec:
containers:
- name: hello
image: "gcr.io/google-samples/hello-go-gke:1.0"
ports:
- name: http
containerPort: 80
백엔드 디플로이먼트를 생성한다.
kubectl apply -f https://k8s.io/examples/service/access/backend-deployment.yaml
백엔드 디플로이먼트에 관한 정보를 본다.
kubectl describe deployment backend
결과는 아래와 같다.
Name: backend
Namespace: default
CreationTimestamp: Mon, 24 Oct 2016 14:21:02 -0700
Labels: app=hello
tier=backend
track=stable
Annotations: deployment.kubernetes.io/revision=1
Selector: app=hello,tier=backend,track=stable
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Pod Template:
Labels: app=hello
tier=backend
track=stable
Containers:
hello:
Image: "gcr.io/google-samples/hello-go-gke:1.0"
Port: 80/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: hello-3621623197 (3/3 replicas created)
Events:
...
hello
서비스 오브젝트 생성하기프론트엔드에서 백엔드로 요청을 보내는 핵심은 백엔드 서비스이다. 서비스는 백엔드 마이크로서비스에 언제든 도달하기 위해 변하지 않는 IP 주소와 DNS 이름 항목을 생성한다. 서비스는 트래픽을 보내는 파드를 찾기 위해 셀렉터를 사용한다.
먼저, 서비스 구성 파일을 살펴보자.
apiVersion: v1
kind: Service
metadata:
name: hello
spec:
selector:
app: hello
tier: backend
ports:
- protocol: TCP
port: 80
targetPort: http
구성 파일에서 hello
라는 이름의 서비스가 app: hello
및 tier: backend
레이블을 갖는
파드에 트래픽을 보내는 것을 볼 수 있다.
백엔드 서비스를 생성한다.
kubectl apply -f https://k8s.io/examples/service/access/backend-service.yaml
이 시점에서 hello
애플리케이션의 복제본 3개를 실행하는 backend
디플로이먼트가 있고, 해당 백엔드로 트래픽을 보내는 서비스가 있다. 그러나, 이
서비스는 클러스터 외부에서 사용할 수 없거나 확인할 수 없다.
이제 백엔드를 실행했으므로, 클러스터 외부에서 접근할 수 있는 프론트엔드를 만들고, 백엔드로의 요청을 프록시하여 백엔드에 연결할 수 있다.
프론트엔드는 백엔드 서비스에 지정된 DNS 이름을 사용하여 백엔드
워커 파드에 요청을 보낸다. DNS 이름은
examples/service/access/backend-service.yaml
구성 파일의
name
필드 값인 hello
이다.
프론트엔드 디플로이먼트 안의 파드는 hello
백엔드 서비스에 대한 요청을
프록시하도록 구성된 nginx 이미지를 실행한다. 다음은 nginx 구성 파일이다.
# The identifier Backend is internal to nginx, and used to name this specific upstream
upstream Backend {
# hello is the internal DNS name used by the backend Service inside Kubernetes
server hello;
}
server {
listen 80;
location / {
# The following statement will proxy traffic to the upstream named Backend
proxy_pass http://Backend;
}
}
백엔드와 같이, 프론트엔드는 디플로이먼트와 서비스를 갖고 있다. 백엔드
서비스와 프론트엔드 서비스 간에 주목해야 할 중요한 차이점은 프론트엔드
서비스의 구성에 type: LoadBalancer
가 있다는 것이다. 즉,
서비스가 클라우드 공급자가 프로비저닝한 로드 밸런서를 사용하고
클러스터 외부에서 접근할 수 있음을 의미한다.
---
apiVersion: v1
kind: Service
metadata:
name: frontend
spec:
selector:
app: hello
tier: frontend
ports:
- protocol: "TCP"
port: 80
targetPort: 80
type: LoadBalancer
...
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
selector:
matchLabels:
app: hello
tier: frontend
track: stable
replicas: 1
template:
metadata:
labels:
app: hello
tier: frontend
track: stable
spec:
containers:
- name: nginx
image: "gcr.io/google-samples/hello-frontend:1.0"
lifecycle:
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"]
...
프론트엔드 디플로이먼트와 서비스를 생성한다.
kubectl apply -f https://k8s.io/examples/service/access/frontend-deployment.yaml
kubectl apply -f https://k8s.io/examples/service/access/frontend-service.yaml
결과는 두 리소스가 생성되었음을 확인한다.
deployment.apps/frontend created
service/frontend created
일단 로드밸런서 타입의 서비스를 생성하면, 이 명령어를 사용해 외부 IP를 찾을 수 있다.
kubectl get service frontend --watch
frontend
서비스의 구성을 보여주고, 변경 사항을
주시한다. 처음에, 외부 IP는 <pending>
으로 나열된다.
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
frontend LoadBalancer 10.51.252.116 <pending> 80/TCP 10s
하지만, 외부 IP가 생성되자마자 구성은
EXTERNAL-IP
제목 아래에 새로운 IP를 포함하여 갱신한다.
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
frontend LoadBalancer 10.51.252.116 XXX.XXX.XXX.XXX 80/TCP 1m
이제 해당 IP는 클러스터 외부에서 frontend
서비스와 통신하는데
사용된다.
이제 프론트엔드와 백엔드가 연결되었다. 프론트엔드 서비스의 외부 IP에서 curl 명령을 사용해 엔드포인트에 도달할 수 있다.
curl http://${EXTERNAL_IP} # 앞의 예에서 본 EXTERNAL-IP로 수정한다
결과로 백엔드에서 생성된 메시지가 보인다.
{"message":"Hello"}
서비스를 삭제하기 위해, 아래 명령어를 입력하자.
kubectl delete services frontend backend
백엔드와 프론트엔드 애플리케이션에서 실행 중인 디플로이먼트, 레플리카셋, 파드를 삭제하기 위해, 아래 명령어를 입력하자.
kubectl delete deployment frontend backend
이 문서는 외부 로드 밸런서를 생성하는 방법에 관하여 설명한다.
서비스를 생성할 때, 클라우드 로드 밸런서를 자동으로 생성하는 옵션을 사용할 수 있다. 이것은 클러스터 노드의 올바른 포트로 트래픽을 전송할 수 있도록 외부에서 접근 가능한 IP 주소를 제공한다. 클러스터가 지원되는 환경과 올바른 클라우드 로드 밸런서 제공자 패키지 구성으로 실행되는 경우.
또한, 서비스 대신 인그레스(Ingress) 를 사용할 수 있다. 자세한 사항은 인그레스(Ingress) 문서를 참고한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
클러스터는 반드시 클라우드 또는 외부 로드 밸런서 구성을 지원하는 환경에서 실행 중이어야 한다.
외부 로드 밸런서를 생성하기 위해서, 서비스 매니페스트에 다음을 추가한다.
type: LoadBalancer
매니페스트는 아래와 같을 것이다.
apiVersion: v1
kind: Service
metadata:
name: example-service
spec:
selector:
app: example
ports:
- port: 8765
targetPort: 9376
type: LoadBalancer
또한, kubectl expose
명령어에 --type=LoadBalancer
플래그를 이용해
서비스를 생성할 수 있다.
kubectl expose deployment example --port=8765 --target-port=9376 \
--name=example-service --type=LoadBalancer
이 명령은 동일한 리소스를 셀렉터로 참조하는 새로운 서비스를 만든다.
(위 예시의 경우, example
로 명명된
디플로이먼트(Deployment) ).
명령줄 옵션 플래그를 포함한, 더 자세한 내용은
kubectl expose
레퍼런스 문서를 참고한다.
kubectl
명령어를 사용해 서비스 정보를 얻어,
생성된 서비스에 관한 IP 주소를 찾을 수 있다.
kubectl describe services example-service
출력은 다음과 같다.
Name: example-service
Namespace: default
Labels: app=example
Annotations: <none>
Selector: app=example
Type: LoadBalancer
IP Families: <none>
IP: 10.3.22.96
IPs: 10.3.22.96
LoadBalancer Ingress: 192.0.2.89
Port: <unset> 8765/TCP
TargetPort: 9376/TCP
NodePort: <unset> 30593/TCP
Endpoints: 172.17.0.3:9376
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
로드 밸런서의 IP 주소는 LoadBalancer Ingress
옆에 나타난다.
만약 서비스가 Minikube에서 실행되고 있다면, 아래의 명령을 통해 할당된 IP 주소와 포트를 찾을 수 있다.
minikube service example-service --url
기본적으로 대상 컨테이너에 보이는 소스 IP는 클라이언트의 원래 소스 IP가 아니다.
클라이언트의 IP를 보존할 수 있도록 하려면,
아래의 서비스 .spec
필드 구성을 따른다.
.spec.externalTrafficPolicy
- 이 서비스가 외부 트래픽을 노드-로컬 또는
클러스터-전체 엔드포인트로 라우팅할지 여부를 나타낸다.
두 가지 옵션이 있다. Cluster
(기본) 그리고 Local
.
Cluster
는 클라이언트 소스 IP를 가리고 다른 노드에 대한
두 번째 홉(hop)을 발생시킬 수 있지만,
전체적인 부하 분산에서 이점이 있다.
Local
은 클라이언트 소스 IP를 보존하고
LoadBalancer
와 NodePort
타입의 서비스에서 두 번째 홉(hop) 발생을 피할 수 있지만,
트래픽 분산이 불균형적인 잠재적인 위험이 있다..spec.healthCheckNodePort
- 서비스를 위한 헬스 체크 노드 포트(정수 포트 번호)를 지정한다.
healthCheckNodePort
를 지정하지 않으면,
서비스 컨트롤러가 클러스터의 노트 포트 범위에서 포트를 할당한다.
API 서버 명령줄 플래그 --service-node-port-range
를 설정하여 해당 범위를 구성할 수 있다.
서비스 type
이 LoadBalancer
이고 externalTrafficPolicy
를 Local
로 설정한 경우,
서비스는 healthCheckNodePort
가 지정되었다면,
사용자가 지정한 설정을 이용한다.서비스 매니페스트에서 externalTrafficPolicy
를 Local
로 설정하면 이 기능이 작동한다.
예시:
apiVersion: v1
kind: Service
metadata:
name: example-service
spec:
selector:
app: example
ports:
- port: 8765
targetPort: 9376
externalTrafficPolicy: Local
type: LoadBalancer
일부 클라우드 제공자의 로드 밸런싱 서비스에서는 대상별로 다른 가중치를 구성할 수 없다.
각 대상의 가중치는 노드로 전송하는 트래픽을 측면에서 균등하게 부여하기 때문에 외부 트래픽은 서로 다른 파드 간에 로드 밸런싱되지 않는다. 외부 로드 밸런서는 각 노드에서 대상으로 사용되는 파드의 개수를 인식하지 못한다.
서비스파드개수 << 노드개수
이거나 서비스파드개수 >> 노드개수
인 경우에선
가중치 없이도 거의 균등한 분포를 볼 수 있다.
내부 파드 간 트래픽은 ClusterIP
서비스에서와 비슷하게 모든 파드에서 동일한 확률로 IP 서비스를 제공한다.
Kubernetes v1.17 [stable]
일반적으로 클라우드 제공자와 관련 있는 로드밸런서 리소스는 type
이 LoadBalancer
인
서비스가 삭제된 후 즉시 정리되어야 한다.
그러나 관련 서비스가 삭제된 후 클라우드 리소스가 고아가 되는 코너 케이스가 다양한 것으로 알려져 있다.
이러한 문제를 예방하기 위해 서비스 로드밸런서를 위한 Finalizer Protection
이 도입되었다.
Finalizer
를 사용하면, 서비스 리소스는 로드밸런서 관련 리소스가 삭제될 때까지 삭제되지 않는다.
특히 서비스에 type
이 LoadBalancer
인 경우
서비스 컨트롤러는 service.kubernetes.io/load-balancer-cleanup
이라는 이름의 finalizer
를 붙인다.
finalizer
는 (클라우드) 로드 밸런서 리소스를 정리한 후에만 제거된다.
이렇게 하면 서비스 컨트롤러 충돌(crash)과 같은 코너 케이스에서도
로드 밸런서 리소스가 고아가 되는 것을 방지할 수 있다.
중요한 점은 이 기능을 위한 데이터 경로는 쿠버네티스 클러스터 외부의 로드 밸런서에서 제공한다는 것이다.
서비스의 type
이 LoadBalancer
로 설정된 경우,
쿠버네티스는 type
이 ClusterIP
인 경우처럼 동등한 기능을 클러스터 내의 파드에 제공하고
관련 쿠버네티스 파드를 호스팅하는 노드에 대한 항목으로 (쿠버네티스 외부) 로드 밸런서를 프로그래밍을 통해 확장한다.
쿠버네티스 컨트롤 플레인은 외부 로드 밸런서, (필요한 경우) 헬스 체크 및 (필요한 경우) 패킷 필터링 규칙의 생성을 자동화한다.
클라우드 공급자가 로드 밸런서에 대한 IP 주소를 할당하면 컨트롤 플레인이 해당 외부 IP 주소를 찾아 서비스 오브젝트를 갱신한다.
인그레스는 클러스터의 서비스에 대한 외부 액세스를 허용하는 규칙을 정의하는 API 객체이다. 인그레스 컨트롤러는 인그레스에 설정된 규칙을 이행한다.
이 페이지에서는 HTTP URI에 따라 요청을 Service web 또는 web2로 라우팅하는 간단한 인그레스를 설정하는 방법을 보여준다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: 1.19. 버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
만약 이보다 더 이전 버전의 쿠버네티스를 사용하고 있다면,
해당 쿠버네티스 버전의 문서를 참고한다.
minikube start
를 실행하여 클러스터를 생성한다.NGINX 인그레스 컨트롤러를 활성화하기 위해 다음 명령을 실행한다.
minikube addons enable ingress
NGINX 인그레스 컨트롤러가 실행 중인지 확인한다.
kubectl get pods -n ingress-nginx
결과는 다음과 같다.
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-g9g49 0/1 Completed 0 11m
ingress-nginx-admission-patch-rqp78 0/1 Completed 1 11m
ingress-nginx-controller-59b45fb494-26npt 1/1 Running 0 11m
kubectl get pods -n kube-system
결과는 다음과 같다.
NAME READY STATUS RESTARTS AGE
default-http-backend-59868b7dd6-xb8tq 1/1 Running 0 1m
kube-addon-manager-minikube 1/1 Running 0 3m
kube-dns-6dcb57bcc8-n4xd4 3/3 Running 0 2m
kubernetes-dashboard-5498ccf677-b8p5h 1/1 Running 0 2m
nginx-ingress-controller-5984b97644-rnkrg 1/1 Running 0 1m
storage-provisioner 1/1 Running 0 2m
nginx-ingress-controller-
로 시작하는 파드가 있는지 확인한다.
다음 명령을 사용하여 디플로이먼트(Deployment)를 생성한다.
kubectl create deployment web --image=gcr.io/google-samples/hello-app:1.0
결과는 다음과 같다.
deployment.apps/web created
디플로이먼트를 노출시킨다.
kubectl expose deployment web --type=NodePort --port=8080
결과는 다음과 같다.
service/web exposed
서비스(Service)가 생성되고 노드 포트에서 사용할 수 있는지 확인한다.
kubectl get service web
결과는 다음과 같다.
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web NodePort 10.104.133.249 <none> 8080:31637/TCP 12m
노드포트(NodePort)를 통해 서비스에 접속한다.
minikube service web --url
결과는 다음과 같다.
http://172.17.0.15:31637
결과는 다음과 같다.
Hello, world!
Version: 1.0.0
Hostname: web-55b8c6998d-8k564
이제 Minikube IP 주소와 노드포트를 통해 샘플 앱에 액세스할 수 있다. 다음 단계에서는 인그레스 리소스를 사용하여 앱에 액세스할 수 있다.
다음 매니페스트는 hello-world.info를 통해 서비스로 트래픽을 보내는 인그레스를 정의한다.
example-ingress.yaml
을 만든다.apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
spec:
ingressClassName: nginx
rules:
- host: hello-world.info
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 8080
다음 명령어를 실행하여 인그레스 오브젝트를 생성한다.
kubectl apply -f https://k8s.io/examples/service/networking/example-ingress.yaml
결과는 다음과 같다.
ingress.networking.k8s.io/example-ingress created
IP 주소가 설정되었는지 확인한다.
kubectl get ingress
다음 예시와 같이, ADDRESS 열에서 IPv4 주소를 확인할 수 있다.
NAME CLASS HOSTS ADDRESS PORTS AGE
example-ingress <none> hello-world.info 172.17.0.15 80 38s
호스트 컴퓨터의 /etc/hosts
파일 맨 아래에
다음 행을 추가한다 (관리자 권한 필요).
172.17.0.15 hello-world.info
이렇게 하면, 웹 브라우저가 hello-world.info URL에 대한 요청을 Minikube로 전송한다.
인그레스 컨트롤러가 트래픽을 전달하는지 확인한다.
curl hello-world.info
결과는 다음과 같다.
Hello, world!
Version: 1.0.0
Hostname: web-55b8c6998d-8k564
다음 명령을 사용하여 두 번째 디플로이먼트를 생성한다.
kubectl create deployment web2 --image=gcr.io/google-samples/hello-app:2.0
결과는 다음과 같다.
deployment.apps/web2 created
두 번째 디플로이먼트를 노출시킨다.
kubectl expose deployment web2 --port=8080 --type=NodePort
결과는 다음과 같다.
service/web2 exposed
기존 example-ingress.yaml
매니페스트를 편집하고,
하단에 다음 줄을 추가한다.
- path: /v2
pathType: Prefix
backend:
service:
name: web2
port:
number: 8080
변경 사항을 적용한다.
kubectl apply -f example-ingress.yaml
결과는 다음과 같다.
ingress.networking/example-ingress configured
Hello World 앱의 첫 번째 버전에 액세스한다.
curl hello-world.info
결과는 다음과 같다.
Hello, world!
Version: 1.0.0
Hostname: web-55b8c6998d-8k564
Hello World 앱의 두 번째 버전에 액세스한다.
curl hello-world.info/v2
결과는 다음과 같다.
Hello, world!
Version: 2.0.0
Hostname: web2-75cd47646f-t8cjk
이 문서는 kubectl을 이용하여 클러스터 내 모든 컨테이너 이미지 목록을 조회하는 방법에 관해 설명한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
이 작업에서는 kubectl을 사용하여 클러스터 내 모든 파드의 정보를 조회하고, 결과값의 서식을 변경하여 각 파드에 대한 컨테이너 이미지 목록으로 재구성할 것이다.
kubectl get pods --all-namespaces
를 사용하여 모든 네임스페이스의 모든 파드 정보를 가져온다.-o jsonpath={.items[*].spec.containers[*].image}
를 사용한다.
이 명령어는 결과값으로 받은 json을 반복적으로 파싱하여,
image
필드만을 출력한다.
tr
, sort
, uniq
tr
을 사용하여 공백을 줄 바꾸기로 대체한다.sort
를 사용하여 결과값을 정렬한다.uniq
를 사용하여 이미지 개수를 합산한다.kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" |\
tr -s '[[:space:]]' '\n' |\
sort |\
uniq -c
이 jsonpath는 다음과 같이 해석할 수 있다.
.items[*]
: 각 결과값에 대하여.spec
: spec 값을 가져온다..containers[*]
: 각 컨테이너에 대하여.image
: image 값을 가져온다.kubectl get pod nginx
라면,
jsonpath에서 .items[*]
부분은 생략해야 하는데, 이는 명령어가 아이템 목록이 아닌
단 한 개의 아이템(여기선 파드)으로 결과값을 주기 때문이다.range
연산을 사용하여 명령어의 결과값에서 각각의 요소들을
반복하여 출력할 수 있다.
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{"\n"}{.metadata.name}{":\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}' |\
sort
특정 레이블에 맞는 파드를 지정하기 위해서 -l 플래그를 사용한다. 아래의
명령어 결과값은 app=nginx
레이블에 일치하는 파드만 출력한다.
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" -l app=nginx
특정 네임스페이스의 파드를 지정하려면, 네임스페이스 플래그를 사용한다.
아래의 명령어 결과값은 kube-system
네임스페이스에 있는 파드만 출력한다.
kubectl get pods --namespace kube-system -o jsonpath="{.items[*].spec.containers[*].image}"
jsonpath의 대안으로 Kubectl은 Go 템플릿을 지원한다. 다음과 같이 결과값의 서식을 지정할 수 있다.
kubectl get pods --all-namespaces -o go-template --template="{{range .items}}{{range .spec.containers}}{{.image}} {{end}}{{end}}"
이 페이지에서는 동일한 파드에서 실행 중인 두 개의 컨테이너 간에 통신할 때에, 어떻게 볼륨을 이용하는지 살펴본다. 컨테이너 간에 프로세스 네임스페이스 공유하기를 통해 통신할 수 있는 방법을 참고하자.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
이 실습에서 두 개의 컨테이너를 실행하는 파드를 생성한다. 이 컨테이너들은 통신에 사용할 수 있는 볼륨을 공유한다. 아래는 이 파드의 구성 파일이다.
apiVersion: v1
kind: Pod
metadata:
name: two-containers
spec:
restartPolicy: Never
volumes:
- name: shared-data
emptyDir: {}
containers:
- name: nginx-container
image: nginx
volumeMounts:
- name: shared-data
mountPath: /usr/share/nginx/html
- name: debian-container
image: debian
volumeMounts:
- name: shared-data
mountPath: /pod-data
command: ["/bin/sh"]
args: ["-c", "echo debian 컨테이너에서 안녕하세요 > /pod-data/index.html"]
이 구성 파일에는 파드가 shared-data
로 명명한 볼륨을 가진 것을
알 수 있다.
첫 번째 컨테이너에는 nginx 웹 서버를 실행하는 구성 파일이 나열되어 있다.
공유 볼륨의 마운트 경로는 /usr/share/nginx/html
이다.
두 번째 컨테이너는 debian 이미지 기반이고, 마운트 경로는 /pod-data
이다.
두 번째 컨테이너는 다음 명령어를 실행한 후에 종료한다.
echo debian 컨테이너에서 안녕하세요 > /pod-data/index.html
두 번째 컨테이너는 index.html
파일을
nginx 웹 서버에서 호스팅하는 문서의 루트 디렉터리(/usr/share/nginx/html/
)에 저장한다.
이제, 파드와 두 개의 컨테이너를 생성한다.
kubectl apply -f https://k8s.io/examples/pods/two-container-pod.yaml
파드와 컨테이너의 정보를 확인한다.
kubectl get pod two-containers --output=yaml
출력의 일부는 다음과 같다.
apiVersion: v1
kind: Pod
metadata:
...
name: two-containers
namespace: default
...
spec:
...
containerStatuses:
- containerID: docker://c1d8abd1 ...
image: debian
...
lastState:
terminated:
...
name: debian-container
...
- containerID: docker://96c1ff2c5bb ...
image: nginx
...
name: nginx-container
...
state:
running:
...
Debian 컨테이너가 종료되었음을 알 수 있고, nginx 컨테이너는 아직 실행 중이다.
nginx 컨테이너의 쉘(shell)을 실행한다.
kubectl exec -it two-containers -c nginx-container -- /bin/bash
쉘에서 nginx 웹 서버가 실행 중인지 확인한다.
root@two-containers:/# apt-get update
root@two-containers:/# apt-get install curl procps
root@two-containers:/# ps aux
출력은 아래와 유사하다.
USER PID ... STAT START TIME COMMAND
root 1 ... Ss 21:12 0:00 nginx: master process nginx -g daemon off;
Debian 컨테이너에서 nginx 웹 서버가 호스팅하는 문서의 루트 디렉터리에 index.html
파일을 생성했었음을 상기하자.
curl
을 이용하여 nginx 웹 서버에 HTTP GET 요청을 보낸다.
root@two-containers:/# curl localhost
출력을 보면, nginx 웹 서버에서 debian 컨테이너에서 쓰여진 웹 페이지를 제공하는 것을 알 수 있다.
debian 컨테이너에서 안녕하세요
파드가 여러 컨테이너를 가질 수 있는 주요 이유는 기본 애플리케이션을 보조할 도우미(helper) 애플리케이션을 제공하기 위해서이다. 도우미 애플리케이션의 일반적인 예로는 데이터를 가지고 오는 경우(data puller)나 데이터를 보내주는 경우(data pusher)이거나 프록시가 있다. 도우미와 기본 애플리케이션은 종종 서로 간에 통신을 해야 할 수 있다. 일반적으로 이는 이번 예제에서 살펴본 것 같이, 공유 파일 시스템을 통하거나, 루프백 네트워크 인터페이스 곧 로컬 호스트(localhost)를 통해서 이뤄진다. 이 패턴의 한가지 예는 웹 서버가 도우미 프로그램과 함께 Git 저장소에서 새 업데이트를 받아오는 경우이다.
이 예제에서 볼륨은 파드의 생명 주기 동안 컨테이너를 위한 통신 방법으로 이용했다. 파드가 삭제되고 재생성되면, 공유 볼륨에 저장된 데이터는 잃어버린다.
합성 컨테이너(composite container) 패턴에 관하여 더 공부한다.
모듈 구조를 위한 합성 컨테이너 구조에 관하여 더 공부한다.
파드에서 저장소로 볼룸을 사용하도록 구성하기에 관하여 확인한다.
볼륨을 확인한다.
파드을 확인한다.
쿠버네티스는 지원하는 모든 환경에서 기본으로 활성화된 DNS 클러스터 애드온을 제공한다. 쿠버네티스 1.11과 이후 버전에서는, CoreDNS가 권장되고 기본적으로 kubeadm과 함께 설치 된다.
쿠버네티스 클러스터의 CoreDNS 설정에 대한 더 많은 정보는, DNS 서비스 사용자화 하기을 본다. kube-dns와 함께 쿠버네티스 DNS를 사용하는 방법을 보여주는 예시는 쿠버네티스 DNS 샘플 플러그인을 본다.
이 페이지는 쿠버네티스 클러스터에서 실행되는 서비스에 연결하는 방법을 보여준다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
쿠버네티스에서, 노드, 파드 및 서비스는 모두 고유한 IP를 가진다. 당신의 데스크탑 PC와 같은 클러스터 외부 장비에서는 클러스터 상의 노드 IP, 파드 IP, 서비스 IP로 라우팅되지 않아서 접근할 수 없을 것이다.
클러스터 외부에서 노드, 파드 및 서비스에 접속하기 위한 몇 가지 옵션이 있다.
NodePort
또는 LoadBalancer
타입의
서비스를 사용한다. 서비스와
kubectl expose 문서를 참고한다.일반적으로 kube-system에 의해 클러스터에 실행되는 몇 가지 서비스가 있다.
kubectl cluster-info
커맨드로 이 서비스의 리스트를 볼 수 있다.
kubectl cluster-info
출력은 다음과 비슷하다.
Kubernetes master is running at https://192.0.2.1
elasticsearch-logging is running at https://192.0.2.1/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy
kibana-logging is running at https://192.0.2.1/api/v1/namespaces/kube-system/services/kibana-logging/proxy
kube-dns is running at https://192.0.2.1/api/v1/namespaces/kube-system/services/kube-dns/proxy
grafana is running at https://192.0.2.1/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
heapster is running at https://192.0.2.1/api/v1/namespaces/kube-system/services/monitoring-heapster/proxy
각 서비스에 접근하기 위한 프록시-작업 URL이 표시된다.
예를 들어, 이 클러스터에는 https://192.0.2.1/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/
로
접근할 수 있는 (Elasticsearch를 사용한) 클러스터 수준 로깅이 활성화되어 있다. 적합한 자격 증명이 전달되는 경우나 kubectl proxy를 통해 도달할 수 있다. 예를 들어 다음의 URL에서 확인할 수 있다.
http://localhost:8080/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/
.
위에서 언급한 것처럼, kubectl cluster-info
명령을 사용하여 서비스의 프록시 URL을 검색한다. 서비스 엔드포인트, 접미사 및 매개 변수를 포함하는 프록시 URL을 작성하려면, 서비스의 프록시 URL에 추가하면 된다.
http://
kubernetes_master_address
/api/v1/namespaces/
namespace_name
/services/
[https:]service_name[:port_name]
/proxy
포트에 대한 이름을 지정하지 않은 경우, URL에 port_name 을 지정할 필요가 없다. 또한, 이름이 지정된 포트와 지정되지 않은 포트 모두에 대해, port_name 자리에 포트 번호를 기재할 수도 있다.
기본적으로, API 서버는 서비스로의 프록시를 HTTP로 제공한다. HTTPS를 사용하려면, 서비스 이름 앞에 https:
를 추가한다.
http://<쿠버네티스_컨트롤_플레인_주소>/api/v1/namespaces/<네임스페이스_이름>/services/<서비스_이름>/proxy
URL에서 <서비스_이름>
이 지원하는 형식은 다음과 같다.
<서비스_이름>
- 기본 포트 또는 이름이 지정되지 않은 포트로 http를 사용하여 프록시<서비스_이름>:<포트_이름>
- 기재된 포트 이름 또는 포트 번호로 http를 사용하여 프록시https:<서비스_이름>:
- 기본 포트 또는 이름이 지정되지 않은 포트로 https를 사용하여 프록시(맨 끝의 콜론에 유의)https:<서비스_이름>:<포트_이름>
- 기재된 포트 이름 또는 포트 번호로 https를 사용하여 프록시Elasticsearch 서비스 엔드포인트 _search?q=user:kimchy
에 접근하려면, 다음을 사용한다.
http://192.0.2.1/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/_search?q=user:kimchy
Elasticsearch 클러스터 상태 정보 _cluster/health?pretty=true
에 접근하려면, 다음을 사용한다.
https://192.0.2.1/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/_cluster/health?pretty=true
상태 정보는 다음과 비슷하다.
{
"cluster_name" : "kubernetes_logging",
"status" : "yellow",
"timed_out" : false,
"number_of_nodes" : 1,
"number_of_data_nodes" : 1,
"active_primary_shards" : 5,
"active_shards" : 5,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 5
}
https Elasticsearch 서비스 상태 정보 _cluster/health?pretty=true
에 접근하려면, 다음을 사용한다.
https://192.0.2.1/api/v1/namespaces/kube-system/services/https:elasticsearch-logging/proxy/_cluster/health?pretty=true
브라우저의 주소 표시줄에 apiserver 프록시 URL을 넣을 수 있다. 그러나,
애그리게이션 레이어(aggregation layer)와 작동하도록 확장 API 서버를 설정하면 쿠버네티스 API 서버를 쿠버네티스의 핵심 API의 일부가 아닌 추가 API로 확장할 수 있다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
다음 단계는 확장 API 서버를 높은 수준 으로 설정하는 방법을 설명한다. 이 단계는 YAML 구성을 사용하거나 API를 사용하는 것에 상관없이 적용된다. 둘 사이의 차이점을 구체적으로 식별하려고 시도한다. YAML 구성을 사용하여 구현하는 방법에 대한 구체적인 예를 보려면, 쿠버네티스 리포지터리에서 sample-apiserver를 참고할 수 있다.
또는, apiserver-builder와 같은 기존의 타사 솔루션을 사용하여 스켈레톤(skeleton)을 생성하고 다음 단계를 모두 자동화해야 한다.
--runtime-config
확인). 클러스터에서 일부러 해제하지 않았다면, 기본적으로 활성화되어 있어야 한다.<service name>.<service name namespace>.svc
형식이다.system:auth-delegator
클러스터 롤로 쿠버네티스 클러스터 롤 바인딩을 만들어 인증 결정을 쿠버네티스 핵심 API 서버에 위임한다.extension-apiserver-authentication-reader
롤로 쿠버네티스 롤 바인딩을 생성한다. 이를 통해 확장 API 서버가 extension-apiserver-authentication
컨피그맵(configmap)에 접근할 수 있다.쿠버네티스는 여기에서 설명한 스케줄러를 기본 스케줄러로 사용한다. 만일 기본 스케줄러가 사용자의 필요를 만족시키지 못한다면 직접 스케줄러를 구현하여 사용할 수 있다. 이에 더해, 기본 스케줄러와 함께 여러 스케줄러를 동시에 사용하여 쿠버네티스가 각 파드에 대해 어떤 스케줄러를 적용할지에 대한 설정도 할 수 있다. 예제와 함께 쿠버네티스에서 다중 스케줄러를 사용하는 방법에 대해 배워보도록 하자.
스케줄러를 구현하는 방법에 대한 자세한 설명은 해당 문서에서 다루지 않는다. kube-scheduler 구현을 다루는 공식 예시는 쿠버네티스 소스 디렉토리에 있는 pkg/scheduler 를 참고한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
스케줄러 바이너리를 컨테이너 이미지로 패키징한다. 해당 예제를 통해 기본 스케줄러 (kube-scheduler)를 두 번째 스케줄러로 사용할 수 있다. GitHub 쿠버네티스 소스코드를 클론하고 소스를 빌드하자.
git clone https://github.com/kubernetes/kubernetes.git
cd kubernetes
make
kube-scheduler 바이너리를 담은 컨테이너 이미지를 생성하자.
이미지를 빌드 하기 위한 Dockerfile
은 다음과 같다.
FROM busybox
ADD ./_output/local/bin/linux/amd64/kube-scheduler /usr/local/bin/kube-scheduler
파일을 Dockerfile
로 저장하고 이미지를 빌드한 후 레지스트리로 푸시하자. 해당 예제에서는 이미지를
Google Container Registry (GCR)로
푸시하고 있다.
이에 대한 자세한 내용은 GCR
문서를 참고하자.
docker build -t gcr.io/my-gcp-project/my-kube-scheduler:1.0 .
gcloud docker -- push gcr.io/my-gcp-project/my-kube-scheduler:1.0
이제 스케줄러 컨테이너 이미지가 있으니, 해당 이미지를 포함하는 파드 구성을 생성하고
쿠버네티스 클러스터 내에서 실행해보자. 해당 예제에서는, 클러스터 내에 직접 파드를 생성하는 대신에
디플로이먼트를 사용해도 된다.
디플로이먼트는
레플리카 셋을 관리하며,
이는 또 파드를 관리하기 때문에 스케줄러에 대한 회복 탄력성을 제공한다.
다음은 디플로이먼트에 대한 구성 파일이다. 이 파일을 my-scheduler.yaml
으로 저장한다.
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-scheduler
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: my-scheduler-as-kube-scheduler
subjects:
- kind: ServiceAccount
name: my-scheduler
namespace: kube-system
roleRef:
kind: ClusterRole
name: system:kube-scheduler
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: my-scheduler-as-volume-scheduler
subjects:
- kind: ServiceAccount
name: my-scheduler
namespace: kube-system
roleRef:
kind: ClusterRole
name: system:volume-scheduler
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ConfigMap
metadata:
name: my-scheduler-config
namespace: kube-system
data:
my-scheduler-config.yaml: |
apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: my-scheduler
leaderElection:
leaderElect: false
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
component: scheduler
tier: control-plane
name: my-scheduler
namespace: kube-system
spec:
selector:
matchLabels:
component: scheduler
tier: control-plane
replicas: 1
template:
metadata:
labels:
component: scheduler
tier: control-plane
version: second
spec:
serviceAccountName: my-scheduler
containers:
- command:
- /usr/local/bin/kube-scheduler
- --config=/etc/kubernetes/my-scheduler/my-scheduler-config.yaml
image: gcr.io/my-gcp-project/my-kube-scheduler:1.0
livenessProbe:
httpGet:
path: /healthz
port: 10259
scheme: HTTPS
initialDelaySeconds: 15
name: kube-second-scheduler
readinessProbe:
httpGet:
path: /healthz
port: 10259
scheme: HTTPS
resources:
requests:
cpu: '0.1'
securityContext:
privileged: false
volumeMounts:
- name: config-volume
mountPath: /etc/kubernetes/my-scheduler
hostNetwork: false
hostPID: false
volumes:
- name: config-volume
configMap:
name: my-scheduler-config
해당 매니페스트에서는 KubeSchedulerConfiguration을
사용하여 구현할 스케줄러의 특성을 정의한다. 이러한 설정은 초기화 과정에서 --config
옵션을 통해 kube-scheduler
에게 전달된다.
해당 구성 파일은 my-scheduler-config
컨피그맵에 저장된다. my-scheduler
디플로이먼트의 파드에서는 my-scheduler-config
컨피그맵을 볼륨으로 마운트 시킨다.
앞서 언급한 스케줄러 구성에서는, 구현한 스케줄러가 KubeSchedulerProfile의 형식으로 나타나게 된다.
spec.schedulerName
필드가 KubeSchedulerProfile
의 schedulerName
필드와 일치하는지 확인해야 한다.
클러스터 내 실행되고 있는 모든 스케줄러는 고유한 이름을 가져야 한다.또한, kube-scheduler
와 같은 권한을 부여받기 위해서는 전용 서비스 어카운트 my-scheduler
를 생성하고
해당 서비스 어카운트를 클러스터롤 system:kube-scheduler
와 바인딩해야 한다.
이외의 커맨드 라인 인자에 대한 자세한 설명은
kube-scheduler 문서에서 참고하고
이외의 사용자 정의 kube-scheduler
구성에 대한 자세한 설명은
스케줄러 구성 레퍼런스
에서 참고한다.
쿠버네티스 클러스터에서 스케줄러를 실행하기 위해서, 위의 구성 파일에서 명시한 디플로이먼트를 쿠버네티스 클러스터 내에 생성한다.
kubectl create -f my-scheduler.yaml
스케줄러 파드가 실행되고 있는지 확인한다.
kubectl get pods --namespace=kube-system
NAME READY STATUS RESTARTS AGE
....
my-scheduler-lnf4s-4744f 1/1 Running 0 2m
...
기본 kube-scheduler 파드와 더불어, my-scheduler 파드가 실행("Running")되고 있다는 것을 목록에서 볼 수 있을 것이다.
리더 선출이 활성화된 상태로 다중 스케줄러를 실행하기 위해서는 다음과 같은 작업을 수행해야 한다.
my-scheduler-config
컨피그맵의 YAML 파일에서 KubeSchedulerConfiguration의 다음과 같은 필드들을 갱신한다.
leaderElection.leaderElect
를 true
로leaderElection.resourceNamespace
를 <lock-object-namespace>
로leaderElection.resourceName
을 <lock-object-name>
으로kube-system
네임스페이스를 사용해도 된다.클러스터 내에 RBAC가 활성화되어 있는 상태라면, system:kube-scheduler
클러스터롤을 업데이트 해야 한다.
다음 예시와 같이, 구현한 스케줄러의 이름을 endpoints
와 leases
리소스에 적용되는 룰의 resourceNames에 추가하자.
kubectl edit clusterrole system:kube-scheduler
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: system:kube-scheduler
rules:
- apiGroups:
- coordination.k8s.io
resources:
- leases
verbs:
- create
- apiGroups:
- coordination.k8s.io
resourceNames:
- kube-scheduler
- my-scheduler
resources:
- leases
verbs:
- get
- update
- apiGroups:
- ""
resourceNames:
- kube-scheduler
- my-scheduler
resources:
- endpoints
verbs:
- delete
- get
- patch
- update
이제 두 번째 스케줄러가 실행되고 있으니, 파드를 몇 개 생성하여 기본 스케줄러 또는 새로 배치한 스케줄러에 의해 스케줄링이 되도록 설정해 보자. 특정 스케줄러를 이용하여 파드를 스케줄링하기 위해서는 해당 파드의 명세에 해당 스케줄러의 이름을 명시해야 한다. 세 가지 예시를 참고해 보자.
스케줄러 이름을 명시하지 않은 파드 명세
apiVersion: v1
kind: Pod
metadata:
name: no-annotation
labels:
name: multischeduler-example
spec:
containers:
- name: pod-with-no-annotation-container
image: registry.k8s.io/pause:2.0
스케줄러 이름을 제공받지 못했다면, 파드는 자동으로 기본 스케줄러에 의해 스케줄링이 수행된다.
해당 파일을 pod1.yaml
로 저장하고 쿠버네티스 클러스터에 제출해 보자.
kubectl create -f pod1.yaml
default-scheduler
를 명시한 파드 명세
apiVersion: v1
kind: Pod
metadata:
name: annotation-default-scheduler
labels:
name: multischeduler-example
spec:
schedulerName: default-scheduler
containers:
- name: pod-with-default-annotation-container
image: registry.k8s.io/pause:2.0
spec.schedulerName
의 값으로 스케줄러 이름을 제공함으로써 스케줄러가 정해진다.
이와 같은 경우에서는, 기본 스케줄러의 이름인 default-scheduler
를 명시하고 있다.
해당 파일을 pod2.yaml
로 저장하고 쿠버네티스 클러스터에 제출해 보자.
kubectl create -f pod2.yaml
my-scheduler
를 명시한 파드 명세
apiVersion: v1
kind: Pod
metadata:
name: annotation-second-scheduler
labels:
name: multischeduler-example
spec:
schedulerName: my-scheduler
containers:
- name: pod-with-second-annotation-container
image: registry.k8s.io/pause:2.0
이와 같은 경우에서는, 직접 배치한 스케줄러 - my-scheduler
를 통해
해당 파드의 스케줄링이 수행되어야 한다는 것을 명시하고 있다.
spec.schedulerName
의 값은 KubeSchedulerProfile
매핑의 schedulerName
필드와 일치해야 한다.
해당 파일을 pod3.yaml
로 저장하고 쿠버네티스 클러스터에 제출해 보자.
kubectl create -f pod3.yaml
세 개의 파드가 모두 실행되고 있는지 확인해 보자.
kubectl get pods
이번 예제들을 수월하게 진행하기 위해,
파드가 실제로 원하는 스케줄러에 의해 스케줄링되고 있는지 확인해 보지 않았다.
해당 사항은 파드와 디플로이먼트 구성 파일의 제출 순서를 바꿔보면 확인해 볼 수 있다.
만일 스케줄러 디플로이먼트 구성 파일을 제출하기 전에 모든 파드의 구성 파일을 쿠버네티스 클러스터에 제출한다면,
다른 두 개의 파드는 스케줄링 되는 와중에 annotation-second-scheduler
파드는
무기한 "Pending" 상태에 머무르는 것을 관찰할 수 있다.
스케줄러 디플로이먼트 구성 파일을 제출하여 새로운 스케줄러가 실행되기 시작하면,
annotation-second-scheduler
파드도 스케줄링 된다.
다른 방법으로는, 이벤트 로그에서 "Scheduled" 항목을 찾아 파드가 원하는 스케줄러에 의해 스케줄링 되었는지 확인해 볼 수 있다.
kubectl get events
또한, 관련된 컨트롤 플레인 노드들의 스태틱 파드 매니페스트를 수정하면 클러스터의 메인 스케줄러로 사용자 정의 스케줄러 구성 또는 사용자 정의 컨테이너 이미지를 사용할 수도 있다.
이 페이지는 쿠버네티스 API에 접근하기 위해 HTTP 프록시를 사용하는 방법을 설명한다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
클러스터에서 실행 중인 애플리케이션이 없다면, 아래 명령을 입력하여 Hello world 애플리케이션을 시작한다.
kubectl create deployment node-hello --image=gcr.io/google-samples/node-hello:1.0 --port=8080
아래 커맨드는 쿠버네티스 API 서버의 프록시를 시작한다.
kubectl proxy --port=8080
프록시 서버가 실행 중일 때 curl
, wget
또는 브라우저를 사용하여 API를 탐색할 수 있다.
API 버전 가져오기.
curl http://localhost:8080/api/
출력은 다음과 유사하다.
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "10.0.2.15:8443"
}
]
}
파드 목록 가져오기.
curl http://localhost:8080/api/v1/namespaces/default/pods
출력은 다음과 유사하다.
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "33074"
},
"items": [
{
"metadata": {
"name": "kubernetes-bootcamp-2321272333-ix8pt",
"generateName": "kubernetes-bootcamp-2321272333-",
"namespace": "default",
"uid": "ba21457c-6b1d-11e6-85f7-1ef9f1dab92b",
"resourceVersion": "33003",
"creationTimestamp": "2016-08-25T23:43:30Z",
"labels": {
"pod-template-hash": "2321272333",
"run": "kubernetes-bootcamp"
},
...
}
kubectl 프록시에 대해 더 배우기.
Kubernetes v1.24 [stable]
이 문서는 SOCKS5 프록시를 사용하여 원격 쿠버네티스 클러스터의 API에 접근하는 방법을 설명한다. 이 기능은 접근하려는 클러스터의 API를 공용 인터넷에 직접 노출하지 않으려고 할 때 유용하다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.24. 버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
SSH 클라이언트 소프트웨어(ssh
도구)와 원격 서버에서 실행되는 SSH 서비스가 필요하다.
원격 서버의 SSH 서비스에 로그인할 수 있어야 한다.
그림 1은 이 작업에서 달성하고자 하는 목표를 나타낸다.
그림 1. SOCKS5 튜토리얼 구성 요소
아래 커맨드(command)는 클라이언트 컴퓨터와 원격 서버 간에 SOCKS5 프록시를 시작한다. SOCKS5 프록시를 사용하여 클러스터의 API 서버에 연결할 수 있다.
# 아래 커맨드를 실행한 후 SSH 터널은 포그라운드(foreground)에서 실행된다.
ssh -D 1080 -q -N username@kubernetes-remote-server.example
-D 1080
: SOCKS 프록시를 로컬 포트 1080으로 연다.-q
: quiet 모드. 경고 및 진단 메시지 대부분을 표시하지 않는다.-N
: 원격 커맨드를 실행하지 않는다. 포트 포워딩에 유용.username@kubernetes-remote-server.example
: 쿠버네티스 클러스터가 실행 중인 원격 SSH 서버.쿠버네티스 API를 사용하려면 먼저, 클라이언트에게 앞에서 만든 SOCKS5 프록시를 통해 쿼리를 전송하도록 지시해야 한다.
https_proxy
환경 변수를 설정하고 실행하는 커맨드를 커맨드라인 툴에 전달한다.
export https_proxy=socks5h://localhost:1080
https_proxy
변수를 설정하면 curl
과 같은 툴은 구성한 프록시를 통해 HTTPS 트래픽을 라우팅한다.
툴이 SOCKS5 프록시를 지원해야 이 기능이 동작한다.
localhost
는 로컬 클라이언트 컴퓨터를 참조하지 않는다.
대신 localhost
라고 알려진 원격 서버의 엔드포인트(endpoint)를 가리킨다.
curl
툴은 호스트 이름을 HTTPS URL에서 SOCKS를 통해 전송하고,
원격 서버는 이것을 로컬(루프백 인터페이스에 속하는 주소)로 처리한다.curl -k -v https://localhost:6443/api
프록시와 함께 공식 쿠버네티스 클라이언트 kubectl
을 사용하려면, ~/.kube/config
파일에서 관련 cluster
항목에 대한 proxy-url
요소를 설정한다.
예시:
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LRMEMMW2 # 가독성을 위해서 축약함
server: https://<API_SERVER_IP_ADRESS>:6443 # "쿠버네티스 API" 서버, 쿠버네티스-원격-서버의 IP 주소
proxy-url: socks5://localhost:1080 # 위 다이어그램의 "SSH SOCKS5 프록시" (SOCKS를 통한 DNS 확인 기능 빌트-인)
name: default
contexts:
- context:
cluster: default
user: default
name: default
current-context: default
kind: Config
preferences: {}
users:
- name: default
user:
client-certificate-data: LS0tLS1CR== # 가독성을 위해서 축약함
client-key-data: LS0tLS1CRUdJT= # 가독성을 위해서 축약함
터널이 동작 중이고 이 클러스터를 사용하는 컨텍스트에서 kubectl
을 사용하는 경우 프록시를 통해 클러스터와 상호 작용할 수 있다. 예시:
kubectl get pods
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-85cb69466-klwq8 1/1 Running 0 5m46s
SSH 포트 포워딩 프로세스가 실행 중인 터미널에서 CTRL+C
를 눌러 프로세스를 중지한다.
터미널에 unset https_proxy
를 입력하여 프록시를 통한 http 트래픽 전송을 중지한다.
Konnectivity 서비스는 컨트롤 플레인에 클러스터 통신을 위한 TCP 수준 프록시를 제공한다.
쿠버네티스 클러스터가 있어야 하며, kubectl 명령줄 도구가 클러스터와 통신하도록 설정되어 있어야 한다. 컨트롤 플레인 호스트가 아닌 두 개 이상의 노드로 구성된 클러스터에서 이 튜토리얼을 수행하는 것을 권장한다. 클러스터가 없다면, minikube를 이용하여 생성할 수 있다.
다음 단계에는 송신(egress) 설정이 필요하다. 예를 들면 다음과 같다.
apiVersion: apiserver.k8s.io/v1beta1
kind: EgressSelectorConfiguration
egressSelections:
# 클러스터에 대한 송신(egress) 트래픽을 제어하기 위해
# "cluster"를 name으로 사용한다. 기타 지원되는 값은 "etcd" 및 "controlplane"이다.
- name: cluster
connection:
# API 서버와 Konnectivity 서버 간의 프로토콜을
# 제어한다. 지원되는 값은 "GRPC" 및 "HTTPConnect"이다. 두 모드 간에
# 최종 사용자가 볼 수 있는 차이점은 없다. 동일한 모드에서 작동하도록
# Konnectivity 서버를 설정해야 한다.
proxyProtocol: GRPC
transport:
# API 서버가 Konnectivity 서버와 통신하는 데 사용하는
# transport를 제어한다. Konnectivity 서버가 API 서버와 동일한 시스템에
# 있는 경우 UDS를 사용하는 것이 좋다. 동일한 UDS 소켓에서
# 수신 대기하도록 Konnectivity 서버를 구성해야 한다.
# 지원되는 다른 전송은 "tcp"이다. TCP 전송을 보호하려면 TLS 구성을 설정해야 한다.
uds:
udsName: /etc/kubernetes/konnectivity-server/konnectivity-server.socket
Konnectivity 서비스를 사용하고 네트워크 트래픽을 클러스터 노드로 보내도록 API 서버를 구성해야 한다.
admin/konnectivity/egress-selector-configuration.yaml
과 같은 송신 구성 파일을 생성한다.--egress-selector-config-file
플래그를
API 서버 송신 구성 파일의 경로로 설정한다.spec:
containers:
volumeMounts:
- name: konnectivity-uds
mountPath: /etc/kubernetes/konnectivity-server
readOnly: false
volumes:
- name: konnectivity-uds
hostPath:
path: /etc/kubernetes/konnectivity-server
type: DirectoryOrCreate
konnectivity-server에 대한 인증서 및 kubeconfig를 생성하거나 얻는다.
예를 들어 OpenSSL 커맨드라인 툴을 사용하여 컨트롤 플레인 호스트에서
클러스터 CA 인증서 /etc/kubernetes/pki/ca.crt
를 사용하여 X.509 인증서를 발급할 수 있다.
openssl req -subj "/CN=system:konnectivity-server" -new -newkey rsa:2048 -nodes -out konnectivity.csr -keyout konnectivity.key
openssl x509 -req -in konnectivity.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out konnectivity.crt -days 375 -sha256
SERVER=$(kubectl config view -o jsonpath='{.clusters..server}')
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config set-credentials system:konnectivity-server --client-certificate konnectivity.crt --client-key konnectivity.key --embed-certs=true
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config set-cluster kubernetes --server "$SERVER" --certificate-authority /etc/kubernetes/pki/ca.crt --embed-certs=true
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config set-context system:konnectivity-server@kubernetes --cluster kubernetes --user system:konnectivity-server
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config use-context system:konnectivity-server@kubernetes
rm -f konnectivity.crt konnectivity.key konnectivity.csr
다음으로 Konnectivity 서버와 에이전트를 배포해야 한다. kubernetes-sigs/apiserver-network-proxy에서 구현을 참조할 수 있다.
컨트롤 플레인 노드에 Konnectivity 서버를 배포한다. 제공된
konnectivity-server.yaml
매니페스트는
쿠버네티스 구성 요소가 클러스터에 스태틱 파드(static Pod)로 배포되었다고 가정한다. 그렇지 않은 경우에는 Konnectivity
서버를 데몬셋(DaemonSet)으로 배포할 수 있다.
apiVersion: v1
kind: Pod
metadata:
name: konnectivity-server
namespace: kube-system
spec:
priorityClassName: system-cluster-critical
hostNetwork: true
containers:
- name: konnectivity-server-container
image: registry.k8s.io/kas-network-proxy/proxy-server:v0.0.32
command: ["/proxy-server"]
args: [
"--logtostderr=true",
# 이것은 egressSelectorConfiguration에 설정된 값과 일치해야 한다.
"--uds-name=/etc/kubernetes/konnectivity-server/konnectivity-server.socket",
# 다음 두 줄은 Konnectivity 서버가 apiserver와
# 동일한 시스템에 배포되고 API 서버의 인증서와
# 키가 지정된 위치에 있다고 가정한다.
"--cluster-cert=/etc/kubernetes/pki/apiserver.crt",
"--cluster-key=/etc/kubernetes/pki/apiserver.key",
# 이것은 egressSelectorConfiguration에 설정된 값과 일치해야 한다.
"--mode=grpc",
"--server-port=0",
"--agent-port=8132",
"--admin-port=8133",
"--health-port=8134",
"--agent-namespace=kube-system",
"--agent-service-account=konnectivity-agent",
"--kubeconfig=/etc/kubernetes/konnectivity-server.conf",
"--authentication-audience=system:konnectivity-server"
]
livenessProbe:
httpGet:
scheme: HTTP
host: 127.0.0.1
port: 8134
path: /healthz
initialDelaySeconds: 30
timeoutSeconds: 60
ports:
- name: agentport
containerPort: 8132
hostPort: 8132
- name: adminport
containerPort: 8133
hostPort: 8133
- name: healthport
containerPort: 8134
hostPort: 8134
volumeMounts:
- name: k8s-certs
mountPath: /etc/kubernetes/pki
readOnly: true
- name: kubeconfig
mountPath: /etc/kubernetes/konnectivity-server.conf
readOnly: true
- name: konnectivity-uds
mountPath: /etc/kubernetes/konnectivity-server
readOnly: false
volumes:
- name: k8s-certs
hostPath:
path: /etc/kubernetes/pki
- name: kubeconfig
hostPath:
path: /etc/kubernetes/konnectivity-server.conf
type: FileOrCreate
- name: konnectivity-uds
hostPath:
path: /etc/kubernetes/konnectivity-server
type: DirectoryOrCreate
그런 다음 클러스터에 Konnectivity 에이전트를 배포한다.
apiVersion: apps/v1
# 에이전트를 Deployment(디플로이먼트)로 배포할 수도 있다. 각 노드에 에이전트가
# 있을 필요는 없다.
kind: DaemonSet
metadata:
labels:
addonmanager.kubernetes.io/mode: Reconcile
k8s-app: konnectivity-agent
namespace: kube-system
name: konnectivity-agent
spec:
selector:
matchLabels:
k8s-app: konnectivity-agent
template:
metadata:
labels:
k8s-app: konnectivity-agent
spec:
priorityClassName: system-cluster-critical
tolerations:
- key: "CriticalAddonsOnly"
operator: "Exists"
containers:
- image: us.gcr.io/k8s-artifacts-prod/kas-network-proxy/proxy-agent:v0.0.16
name: konnectivity-agent
command: ["/proxy-agent"]
args: [
"--logtostderr=true",
"--ca-cert=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt",
# konnectivity 서버는 hostNetwork=true로 실행되기 때문에,
# 이것은 마스터 머신의 IP 주소이다.
"--proxy-server-host=35.225.206.7",
"--proxy-server-port=8132",
"--admin-server-port=8133",
"--health-server-port=8134",
"--service-account-token-path=/var/run/secrets/tokens/konnectivity-agent-token"
]
volumeMounts:
- mountPath: /var/run/secrets/tokens
name: konnectivity-agent-token
livenessProbe:
httpGet:
port: 8134
path: /healthz
initialDelaySeconds: 15
timeoutSeconds: 15
serviceAccountName: konnectivity-agent
volumes:
- name: konnectivity-agent-token
projected:
sources:
- serviceAccountToken:
path: konnectivity-agent-token
audience: system:konnectivity-server
마지막으로 클러스터에서 RBAC가 활성화된 경우 관련 RBAC 규칙을 생성한다.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system:konnectivity-server
labels:
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: system:konnectivity-server
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: konnectivity-agent
namespace: kube-system
labels:
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
이 페이지는 kubelet에 대한 인증서 갱신을 활성화하고 구성하는 방법을 보여준다.
Kubernetes v1.19 [stable]
kubelet은 쿠버네티스 API 인증을 위해 인증서를 사용한다. 기본적으로 이러한 인증서는 1년 만기로 발급되므로 너무 자주 갱신할 필요는 없다.
쿠버네티스는 kubelet 인증서 갱신을 포함하며, 이 기능은 현재 인증서의 만료 시한이 임박한 경우, 새로운 키를 자동으로 생성하고 쿠버네티스 API에서 새로운 인증서를 요청하는 기능이다. 새로운 인증서를 사용할 수 있게 되면 쿠버네티스 API에 대한 연결을 인증하는데 사용된다.
kubelet
프로세스는 현재 사용 중인 인증서의 만료 시한이 다가옴에 따라
kubelet이 자동으로 새 인증서를 요청할지 여부를 제어하는
--rotate-certificates
인자를 허용한다.
kube-controller-manager
프로세스는 얼마나 오랜 기간 인증서가 유효한지를 제어하는
--cluster-signing-duration
(1.19 이전은 --experimental-cluster-signing-duration
)
인자를 허용한다.
kubelet이 시작할 때 부트 스트랩 (--bootstrap-kubeconfig
플래그를 사용)
을 구성하면 초기 인증서를 사용하여 쿠버네티스 API에 연결하고
인증서 서명 요청을 발행한다.
다음을 사용하여 인증서 서명 요청 상태를 볼 수 있다.
kubectl get csr
초기에 노드의 kubelet에서 인증서 서명 요청은 Pending
상태이다.
인증서 서명 요청이 특정 기준을 충족하면 컨트롤러 관리자가
자동으로 승인한 후 상태가 Approved
가 된다.
다음으로, 컨트롤러 관리자는
--cluster-signing-duration
파라미터에 의해 지정된 기간 동안
발행된 인증서에 서명하고
서명된 인증서는 인증서 서명 요청에 첨부된다.
kubelet은 쿠버네티스 API로 서명된 인증서를 가져와서
--cert-dir
에 지정된 위치에 디스크에 기록한다.
그런 다음 kubelet은 쿠버네티스 API에 연결해서 새로운 인증서를 사용한다.
서명된 인증서의 만료가 다가오면 kubelet은 쿠버네티스 API를 사용하여 새로운 인증서 서명 요청을 자동으로 발행한다. 이는 인증서 유효 기간이 30%-10% 남은 시점에 언제든지 실행될 수 있다. 또한, 컨트롤러 관리자는 인증서 요청을 자동으로 승인하고 서명된 인증서를 인증서 서명 요청에 첨부한다. kubelet은 쿠버네티스 API로 서명된 새로운 인증서를 가져와서 디스크에 쓴다. 그런 다음 새로운 인증서를 사용한 재연결을 위해서 가지고 있는 쿠버네티스 API로의 연결을 업데이트 한다.
쿠버네티스는 사용자가 제어하는 인증 기관 (CA)에서 서명한 TLS 인증서를
프로비저닝 할 수 있는 certificates.k8s.io
API를 제공한다.
이러한 CA 및 인증서는 워크로드 간의 신뢰 관계를 구성하는 용도로 사용할 수 있다.
certificates.k8s.io
API는 ACME 초안과
유사한 프로토콜을 사용한다.
certificates.k8s.io
API를 사용하여 생성된 인증서는 전용 CA로 서명된다.
이러한 목적을 위해 클러스터 루트 CA를 사용하도록 클러스터를
구성할 수 있지만, 절대 이에 의존해서는 안된다.
해당 인증서가 클러스터 루트 CA에 대해 유효성을 검사한다고 가정하면 안된다.쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
cfssl
도구가 필요하다.
https://github.com/cloudflare/cfssl/releases에서 cfssl
을 다운로드할 수 있다.
이 페이지의 일부 단계에서 jq
도구를 사용한다.
jq
가 없다면, 사용 중인 운영 체제의 소프트웨어 소스를 통해 설치하거나,
https://stedolan.github.io/jq/에서 받을 수 있다.
파드로 실행되는 애플리케이션에서 사용자 정의 CA를 신뢰하려면
일반적으로 몇 가지 추가 애플리케이션 구성이 필요하다.
TLS 클라이언트 또는 서버가 신뢰하는 CA 인증서 목록에
CA 인증서 번들을 추가해야 한다.
예를 들어 인증서 체인을 파싱하고, 파싱된 인증서를 tls.Config
구조체의
RootCAs
필드에 추가하여, golang TLS 구성으로 이를 수행할 수 있다.
사용자 정의 CA 인증서가
파일시스템(kube-root-ca.crt
컨피그맵 내)에 포함될 수 있더라도,
이 인증서 권한을 내부 쿠버네티스 엔드포인트 검증 용도 외에는 사용하지 말아야 한다.
내부 쿠버네티스 엔드포인트에 대한 하나의 예시는
기본 네임스페이스에 있는 kubernetes
라는 서비스이다.
당신의 워크로드를 위한 사용자 정의 인증서 권한을 사용하고 싶다면, 이 CA를 별도로 생성하고, 파드가 읽을 수 있는 컨피그맵 형태로 해당 CA 인증서를 배포해야 한다.
다음 섹션에서는 DNS를 통해 액세스되는 쿠버네티스 서비스의 TLS 인증서를 생성하는 방법을 보여준다.
다음 명령을 실행하여 개인 키 및 인증서 서명 요청(또는 CSR)을 생성한다.
cat <<EOF | cfssl genkey - | cfssljson -bare server
{
"hosts": [
"my-svc.my-namespace.svc.cluster.local",
"my-pod.my-namespace.pod.cluster.local",
"192.0.2.24",
"10.0.34.2"
],
"CN": "my-pod.my-namespace.pod.cluster.local",
"key": {
"algo": "ecdsa",
"size": 256
}
}
EOF
여기서 192.0.2.24
는 서비스의 클러스터 IP,
my-svc.my-namespace.svc.cluster.local
은 서비스의 DNS 이름,
10.0.34.2
는 파드의 IP,my-pod.my-namespace.pod.cluster.local
은
파드의 DNS 이름이다. 다음과 비슷한 출력이 표시되어야 한다.
2022/02/01 11:45:32 [INFO] generate received request
2022/02/01 11:45:32 [INFO] received CSR
2022/02/01 11:45:32 [INFO] generating key: ecdsa-256
2022/02/01 11:45:32 [INFO] encoded CSR
이 명령은 두 개의 파일을 생성한다. PEM으로
인코딩된 PKCS#10
인증 요청이 포함된 server.csr
과 생성할 인증서 키를 PEM 인코딩한 값이
포함된 server-key.pem
을 생성한다.
CSR 매니페스트(YAML 형태)를 생성하고, API 서버로 전송한다. 다음 명령어를 실행하여 수행할 수 있다.
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: my-svc.my-namespace
spec:
request: $(cat server.csr | base64 | tr -d '\n')
signerName: example.com/serving
usages:
- digital signature
- key encipherment
- server auth
EOF
1단계에서 만든 server.csr
파일은 base64로 인코딩되고
.spec.request
필드에 숨겨져 있다.
또한 예시 example.com/serving
서명자가 서명한
"digitalSignature", "keyEnciperment" 및 "serverAuth" 키 사용(keyUsage)이 있는 인증서를 요청한다.
특정 signerName
을 요청해야 한다.
자세한 내용은 지원되는 서명자 이름
문서를 참조한다.
이제 CSR이 API에서 보류 상태로 표시되어야 한다. 다음을 실행하여 확인할 수 있다.
kubectl describe csr my-svc.my-namespace
Name: my-svc.my-namespace
Labels: <none>
Annotations: <none>
CreationTimestamp: Tue, 01 Feb 2022 11:49:15 -0500
Requesting User: yourname@example.com
Signer: example.com/serving
Status: Pending
Subject:
Common Name: my-pod.my-namespace.pod.cluster.local
Serial Number:
Subject Alternative Names:
DNS Names: my-pod.my-namespace.pod.cluster.local
my-svc.my-namespace.svc.cluster.local
IP Addresses: 192.0.2.24
10.0.34.2
Events: <none>
인증서 서명 요청 승인은
자동화된 승인 프로세스 또는 클러스터 관리자의 일회성 승인으로 수행된다.
인증서 요청 승인 권한을 갖고 있다면,
kubectl
을 이용하여 다음과 같이 수동으로 승인할 수 있다.
kubectl certificate approve my-svc.my-namespace
certificatesigningrequest.certificates.k8s.io/my-svc.my-namespace approved
출력은 다음과 같을 것이다.
kubectl get csr
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
my-svc.my-namespace 10m example.com/serving yourname@example.com <none> Approved
이는 즉 인증서 요청이 승인되었으며 요청받은 서명자의 서명을 기다리고 있음을 나타낸다.
다음으로, 인증서 서명자로서, 인증서를 발급하고, 이를 API에 업로드할 것이다.
서명자는 일반적으로 인증서 서명 요청 API를 조회하여 오브젝트의 signerName
을 확인하고,
요청이 승인되었는지 체크하고, 해당 요청에 대해 인증서에 서명하고,
발급된 인증서로 API 오브젝트 상태를 업데이트한다.
새 인증서의 디지털 서명에 제공할 인증 기관이 필요하다.
먼저, 다음을 실행하여 서명 인증서를 생성한다.
cat <<EOF | cfssl gencert -initca - | cfssljson -bare ca
{
"CN": "My Example Signer",
"key": {
"algo": "rsa",
"size": 2048
}
}
EOF
출력은 다음과 같을 것이다.
2022/02/01 11:50:39 [INFO] generating a new CA key and certificate from CSR
2022/02/01 11:50:39 [INFO] generate received request
2022/02/01 11:50:39 [INFO] received CSR
2022/02/01 11:50:39 [INFO] generating key: rsa-2048
2022/02/01 11:50:39 [INFO] encoded CSR
2022/02/01 11:50:39 [INFO] signed certificate with serial number 263983151013686720899716354349605500797834580472
이제 인증 기관 키 파일(ca-key.pem
)과 인증서(ca.pem
)가 생성되었다.
{
"signing": {
"default": {
"usages": [
"digital signature",
"key encipherment",
"server auth"
],
"expiry": "876000h",
"ca_constraint": {
"is_ca": false
}
}
}
}
server-signing-config.json
서명 구성 파일, 인증 기관 키 파일 및 인증서를 사용하여
인증서 요청에 서명한다.
kubectl get csr my-svc.my-namespace -o jsonpath='{.spec.request}' | \
base64 --decode | \
cfssl sign -ca ca.pem -ca-key ca-key.pem -config server-signing-config.json - | \
cfssljson -bare ca-signed-server
출력은 다음과 같을 것이다.
2022/02/01 11:52:26 [INFO] signed certificate with serial number 576048928624926584381415936700914530534472870337
이제 서명된 제공(serving) 인증서 파일 ca-signed-server.pem
이 생성되었다.
마지막으로, 서명된 인증서를 API 오브젝트의 상태에 기재한다.
kubectl get csr my-svc.my-namespace -o json | \
jq '.status.certificate = "'$(base64 ca-signed-server.pem | tr -d '\n')'"' | \
kubectl replace --raw /apis/certificates.k8s.io/v1/certificatesigningrequests/my-svc.my-namespace/status -f -
.status.certificate
필드에 base64로 인코딩된 내용을 채우기 위해
jq
명령줄 도구를 사용하였다.
jq
가 없다면, JSON 출력을 파일에 저장하고,
해당 필드를 수동으로 채우고, 그 결과 파일을 업로드할 수도 있다.인증서 서명 요청이 승인되고 서명된 인증서가 업로드되면 다음을 실행한다.
kubectl get csr
출력은 다음과 같을 것이다.
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
my-svc.my-namespace 20m example.com/serving yourname@example.com <none> Approved,Issued
이제, 요청하는 사용자로서, 다음 명령을 실행하여
발급된 인증서를 다운로드하고 server.crt
파일에 저장할 수 있다.
kubectl get csr my-svc.my-namespace -o jsonpath='{.status.certificate}' \
| base64 --decode > server.crt
이제 server.crt
및 server-key.pem
의 내용으로
시크릿을 생성할 수 있으며,
이 시크릿은 추후 파드에 마운트할 수 있다(예를 들어,
HTTPS를 제공하는 웹서버에 사용).
kubectl create secret tls server --cert server.crt --key server-key.pem
secret/server created
마지막으로, ca.pem
의 내용으로 컨피그맵을 생성하여
제공(serving) 인증서 검증에 필요한 신뢰 루트로 사용할 수 있다.
kubectl create configmap example-serving-ca --from-file ca.crt=ca.pem
configmap/example-serving-ca created
(적절한 권한이 있는) 쿠버네티스 관리자는
kubectl certificate approve
과 kubectl certificate deny
명령을 사용하여 CertificateSigningRequest을 수동으로 승인 (또는 거부) 할 수 있다.
그러나 이 API를 많이 사용한다면,
자동화된 인증서 컨트롤러 작성을 고려할 수 있다.
CSR을 승인할 수 있는 권한이 있다는 것은 당신의 환경에서 누가 누구를 신뢰할 수 있는지를 결정할 수 있다는 것이다. CSR을 승인할 수 있는 권한은 넓게/가볍게 부여되지 않아야 한다.
approve
권한을 부여하기 전에,
승인자에게 할당되는 검증 요구 사항 및 특정 인증서 발급에 따른 영향을
모두 확실히 이해하고 있는지 확인해야 한다.
위와 같이 kubectl을 사용하는 시스템이든 사람이든, 승인자 의 역할은 CSR이 다음 두 가지 요구 사항을 충족하는지 확인하는 것이다.
이 두 가지 요구 사항이 충족되는 경우에만, 승인자가 CSR을 승인하고 그렇지 않으면 CSR을 거부해야 한다.
인증서 승인 및 접근 제어에 대한 추가 정보를 보려면, 인증서 서명 요청 레퍼런스 페이지를 참조한다.
이 페이지에서는 서명자가 인증서 API를 제공하도록 설정되었다고 가정한다. 쿠버네티스
컨트롤러 관리자는 서명자의 기본 구현을 제공한다. 이를
활성화하려면 인증 기관(CA)의 키 쌍에 대한 경로와 함께 --cluster-signing-cert-file
와
--cluster-signing-key-file
매개 변수를
컨트롤러 관리자에 전달한다.
이 페이지는 데몬셋에서 롤링 업데이트를 수행하는 방법을 보여준다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
데몬셋에는 두 가지 업데이트 전략 유형이 있다.
OnDelete
: OnDelete
업데이트 전략을 사용하여, 데몬셋 템플릿을 업데이트한 후,
이전 데몬셋 파드를 수동으로 삭제할 때 만 새 데몬셋 파드가
생성된다. 이것은 쿠버네티스 버전 1.5 이하에서의 데몬셋의 동작과
동일하다.RollingUpdate
: 기본 업데이트 전략이다.
RollingUpdate
업데이트 전략을 사용하여, 데몬셋 템플릿을
업데이트한 후, 오래된 데몬셋 파드가 종료되고, 새로운 데몬셋 파드는
제어 방식으로 자동 생성된다. 전체 업데이트 프로세스 동안
데몬셋의 최대 하나의 파드가 각 노드에서 실행된다.데몬셋의 롤링 업데이트 기능을 사용하려면,
.spec.updateStrategy.type
에 RollingUpdate
를 설정해야 한다.
.spec.updateStrategy.rollingUpdate.maxUnavailable
(기본값은 1),
.spec.minReadySeconds
(기본값은 0),
.spec.updateStrategy.rollingUpdate.maxSurge
(기본값은 0)를
설정할 수도 있다.
RollingUpdate
업데이트 전략으로 데몬셋 생성이 YAML 파일은 'RollingUpdate'를 업데이트 전략으로 사용하여 데몬셋을 명시한다.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
# 이 톨러레이션(toleration)은 데몬셋이 컨트롤 플레인 노드에서 실행될 수 있도록 만든다.
# 컨트롤 플레인 노드가 이 파드를 실행해서는 안 되는 경우, 이 톨러레이션을 제거한다.
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
데몬셋 매니페스트의 업데이트 전략을 확인한 후, 데몬셋을 생성한다.
kubectl create -f https://k8s.io/examples/controllers/fluentd-daemonset.yaml
또는, kubectl apply
로 데몬셋을 업데이트하려는 경우, 동일한 데몬셋을
생성하는 데 kubectl apply
를 사용한다.
kubectl apply -f https://k8s.io/examples/controllers/fluentd-daemonset.yaml
RollingUpdate
업데이트 전략 확인데몬셋의 업데이트 전략을 확인하고, RollingUpdate
로 설정되어 있는지
확인한다.
kubectl get ds/fluentd-elasticsearch -o go-template='{{.spec.updateStrategy.type}}{{"\n"}}' -n kube-system
시스템에서 데몬셋을 생성하지 않은 경우, 대신 다음의 명령으로 데몬셋 매니페스트를 확인한다.
kubectl apply -f https://k8s.io/examples/controllers/fluentd-daemonset.yaml --dry-run=client -o go-template='{{.spec.updateStrategy.type}}{{"\n"}}'
두 명령의 출력 결과는 다음과 같아야 한다.
RollingUpdate
출력 결과가 RollingUpdate
가 아닌 경우, 이전 단계로 돌아가서 데몬셋 오브젝트나 매니페스트를
적절히 수정한다.
RollingUpdate
데몬셋 .spec.template
에 대한 업데이트는 롤링 업데이트를
트리거한다. 새 YAML 파일을 적용하여 데몬셋을 업데이트한다. 이것은 여러 가지 다른 kubectl
명령으로 수행할 수 있다.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
# 이 톨러레이션(toleration)은 데몬셋이 컨트롤 플레인 노드에서 실행될 수 있도록 만든다.
# 컨트롤 플레인 노드가 이 파드를 실행해서는 안 되는 경우, 이 톨러레이션을 제거한다.
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
구성 파일을
사용하여 데몬셋을 업데이트하는 경우,
kubectl apply
를 사용한다.
kubectl apply -f https://k8s.io/examples/controllers/fluentd-daemonset-update.yaml
명령형 커맨드를
사용하여 데몬셋을 업데이트하는 경우,
kubectl edit
를 사용한다.
kubectl edit ds/fluentd-elasticsearch -n kube-system
데몬셋 템플릿(예: .spec.template.spec.containers[*].image
)에 의해 정의된 컨테이너 이미지만 업데이트하려면,
kubectl set image
를 사용한다.
kubectl set image ds/fluentd-elasticsearch fluentd-elasticsearch=quay.io/fluentd_elasticsearch/fluentd:v2.6.0 -n kube-system
마지막으로, 최신 데몬셋 롤링 업데이트의 롤아웃 상태를 관찰한다.
kubectl rollout status ds/fluentd-elasticsearch -n kube-system
롤아웃이 완료되면, 출력 결과는 다음과 비슷하다.
daemonset "fluentd-elasticsearch" successfully rolled out
가끔씩, 데몬셋 롤링 업데이트가 더 이상 진행되지 않을 수 있다. 이와 같은 상황이 발생할 수 있는 원인은 다음과 같다.
적어도 하나의 노드에서 새 데몬셋 파드를 스케줄링할 수 없어서 롤아웃이 중단되었다. 노드에 리소스가 부족할 때 발생할 수 있다.
이 경우, kubectl get nodes
의 출력 결과와 다음의 출력 결과를 비교하여
데몬셋 파드가 스케줄링되지 않은 노드를 찾는다.
kubectl get pods -l name=fluentd-elasticsearch -o wide -n kube-system
해당 노드를 찾으면, 데몬셋이 아닌 파드를 노드에서 삭제하여 새 데몬셋 파드를 위한 공간을 생성한다.
최근 데몬셋 템플릿 업데이트가 중단된 경우(예를 들어, 컨테이너가 계속 크래시되거나, 컨테이너 이미지가 존재하지 않는 경우(종종 오타로 인해)), 데몬셋 롤아웃이 진행되지 않는다.
이 문제를 해결하려면, 데몬셋 템플릿을 다시 업데이트한다. 이전의 비정상 롤아웃으로 인해 새로운 롤아웃이 차단되지는 않는다.
데몬셋에 .spec.minReadySeconds
가 명시된 경우, 마스터와 노드 사이의
클럭 차이로 인해 데몬셋이 올바른 롤아웃 진행 상황을 감지할 수
없다.
네임스페이스에서 데몬셋을 삭제한다.
kubectl delete ds fluentd-elasticsearch -n kube-system
이 페이지는 데몬셋에서 롤백을 수행하는 방법을 보여준다.
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: 1.7. 버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
데몬셋에서 롤링 업데이트를 수행하는 방법을 이미 알고 있어야 한다.
마지막 리비전으로 롤백하려는 경우 이 단계를 건너뛸 수 있다.
데몬셋의 모든 리비전을 나열한다.
kubectl rollout history daemonset <daemonset-name>
이 명령은 데몬셋 리비전 목록을 반환한다.
daemonsets "<daemonset-name>"
REVISION CHANGE-CAUSE
1 ...
2 ...
...
kubernetes.io/change-cause
에서
생성 시의 리비전으로 복사된다. 변경 원인 어노테이션에서 실행된 명령을 기록하도록
kubectl
에 --record=true
를 지정할 수 있다.특정 리비전의 세부 사항을 보려면 다음을 수행한다.
kubectl rollout history daemonset <daemonset-name> --revision=1
이 명령은 해당 리비전의 세부 사항을 반환한다.
daemonsets "<daemonset-name>" with revision #1
Pod Template:
Labels: foo=bar
Containers:
app:
Image: ...
Port: ...
Environment: ...
Mounts: ...
Volumes: ...
# --to-revision에 1단계에서 얻는 리비전 번호를 지정한다
kubectl rollout undo daemonset <daemonset-name> --to-revision=<revision>
성공하면, 명령은 다음을 반환한다.
daemonset "<daemonset-name>" rolled back
--to-revision
플래그를 지정하지 않은 경우, kubectl은 가장 최신의 리비전을 선택한다.kubectl rollout undo daemonset
은 서버에 데몬셋 롤백을 시작하도록
지시한다. 실제 롤백은 클러스터 컨트롤 플레인
내에서 비동기적으로 수행된다.
롤백 진행 상황을 보려면 다음의 명령을 수행한다.
kubectl rollout status ds/<daemonset-name>
롤백이 완료되면, 출력 결과는 다음과 비슷하다.
daemonset "<daemonset-name>" successfully rolled out
이전 kubectl rollout history
단계에서, 데몬셋 리비전 목록을
얻었다. 각 리비전은 ControllerRevision이라는 리소스에 저장된다.
각 리비전에 저장된 내용을 보려면, 데몬셋 리비전 원시 리소스를 찾는다.
kubectl get controllerrevision -l <daemonset-selector-key>=<daemonset-selector-value>
이 명령은 ControllerRevision의 목록을 반환한다.
NAME CONTROLLER REVISION AGE
<daemonset-name>-<revision-hash> DaemonSet/<daemonset-name> 1 1h
<daemonset-name>-<revision-hash> DaemonSet/<daemonset-name> 2 1h
각 ControllerRevision은 데몬셋 리비전의 어노테이션과 템플릿을 저장한다.
kubectl rollout undo
는 특정 ControllerRevision을 가져와 데몬셋
템플릿을 ControllerRevision에 저장된 템플릿으로 바꾼다.
kubectl rollout undo
는 kubectl edit
또는 kubectl apply
와 같은 다른 명령을 통해
데몬셋 템플릿을 이전 리비전으로 업데이트하는 것과
같다.
.revision
필드)가 증가한다. 예를 들어,
시스템에 리비전 1과 2가 있고, 리비전 2에서 리비전 1으로 롤백하면,
ControllerRevision은 .revision: 1
에서 .revision: 3
이 된다.파드의 /etc/hosts
파일에 항목을 추가하는 것은 DNS나 다른 방법들이 적용되지 않을 때 파드 수준의 호스트네임 해석을 제공한다. PodSpec의 HostAliases 항목을 사용하여 이러한 사용자 정의 항목들을 추가할 수 있다.
HostAliases를 사용하지 않은 수정은 권장하지 않는데, 이는 호스트 파일이 kubelet에 의해 관리되고, 파드 생성/재시작 중에 덮어쓰여질 수 있기 때문이다.
파드 IP가 할당된 Nginx 파드를 시작한다.
kubectl run nginx --image nginx
pod/nginx created
파드 IP를 확인해보자.
kubectl get pods --output=wide
NAME READY STATUS RESTARTS AGE IP NODE
nginx 1/1 Running 0 13s 10.200.0.4 worker0
호스트 파일의 내용은 아래와 같을 것이다.
kubectl exec nginx -- cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
10.200.0.4 nginx
기본적으로, hosts
파일은 localhost
와 자기 자신의 호스트네임과 같은 IPv4와 IPv6
상용구들만 포함하고 있다.
기본 상용구 이외에, 추가 항목들을 hosts
파일에
추가할 수 있다.
예를 들어, foo.local
, bar.local
이 127.0.0.1
로,
foo.remote
, bar.remote
가 10.1.2.3
로 해석될 수 있도록, .spec.hostAliases
항목에서 정의하여 파드에
HostAliases를 추가하면 가능하다.
apiVersion: v1
kind: Pod
metadata:
name: hostaliases-pod
spec:
restartPolicy: Never
hostAliases:
- ip: "127.0.0.1"
hostnames:
- "foo.local"
- "bar.local"
- ip: "10.1.2.3"
hostnames:
- "foo.remote"
- "bar.remote"
containers:
- name: cat-hosts
image: busybox:1.28
command:
- cat
args:
- "/etc/hosts"
다음을 실행하여 해당 구성으로 파드를 실행할 수 있다.
kubectl apply -f https://k8s.io/examples/service/networking/hostaliases-pod.yaml
pod/hostaliases-pod created
파드의 세부 정보를 검토하여 IPv4 주소와 상태를 확인해보자.
kubectl get pod --output=wide
NAME READY STATUS RESTARTS AGE IP NODE
hostaliases-pod 0/1 Completed 0 6s 10.200.0.5 worker0
hosts
파일 내용은 아래와 같다.
kubectl logs hostaliases-pod
# Kubernetes-managed hosts file.
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
10.200.0.5 hostaliases-pod
# Entries added by HostAliases.
127.0.0.1 foo.local bar.local
10.1.2.3 foo.remote bar.remote
가장 마지막에 추가 항목들이 정의되어 있는 것을 확인할 수 있다.
컨테이너가 이미 시작되고 난 뒤에
컨테이너 런타임이 hosts
파일을 수정하는 것을 방지하기 위해,
Kubelet이 파드의 각 컨테이너의 hosts
파일을 관리한다.
역사적으로, 쿠버네티스는 컨테이너 런타임으로 계속 도커 엔진을 사용해 왔으며,
각 컨테이너가 시작된 뒤에 도커 엔진이 /etc/hosts
파일을 수정할 수 있었다.
현재 쿠버네티스는 다양한 컨테이너 런타임을 사용할 수 있으며,
kubelet이 각 컨테이너 내의 hosts
파일을 관리하므로
어떤 컨테이너 런타임을 사용하는지에 상관없이 동일한 결과를 얻을 수 있다.
컨테이너 내부의 호스트 파일을 수동으로 변경하면 안된다.
호스트 파일을 수동으로 변경하면, 컨테이너가 종료되면 변경 사항이 손실된다.
이 문서는 IPv4/IPv6 이중 스택이 활성화된 쿠버네티스 클러스터들을 어떻게 검증하는지 설명한다.
kubectl version
.
각각의 이중 스택 노드는 단일 IPv4 블록 및 단일 IPv6 블록을 할당받아야 한다. IPv4/IPv6 파드 주소 범위를 다음 커맨드를 실행하여 검증한다. 샘플 노드 이름을 클러스터 내 검증된 이중 스택 노드로 대체한다. 본 예제에서, 노드 이름은 k8s-linuxpool1-34450317-0
이다.
kubectl get nodes k8s-linuxpool1-34450317-0 -o go-template --template='{{range .spec.podCIDRs}}{{printf "%s\n" .}}{{end}}'
10.244.1.0/24
2001:db8::/64
단일 IPv4 블록과 단일 IPv6 블록이 할당되어야 한다.
노드가 IPv4 및 IPv6 인터페이스를 가지고 있는지 검증한다. 노드 이름을 클러스터의 검증된 노드로 대체한다. 본 예제에서 노드 이름은 k8s-linuxpool1-34450317-0
이다.
kubectl get nodes k8s-linuxpool1-34450317-0 -o go-template --template='{{range .status.addresses}}{{printf "%s: %s\n" .type .address}}{{end}}'
Hostname: k8s-linuxpool1-34450317-0
InternalIP: 10.0.0.5
InternalIP: 2001:db8:10::5
파드가 IPv4 및 IPv6 주소를 할당받았는지 검증한다. 파드 이름을 클러스터에서 검증된 파드로 대체한다. 본 예제에서 파드 이름은 pod01
이다.
kubectl get pods pod01 -o go-template --template='{{range .status.podIPs}}{{printf "%s\n" .ip}}{{end}}'
10.244.1.4
2001:db8::4
status.podIPs
fieldPath를 통한 다운워드(downward) API로 파드 IP들을 검증할 수도 있다. 다음 스니펫은 컨테이너 내 MY_POD_IPS
라는 환경 변수를 통해 파드 IP들을 어떻게 노출시킬 수 있는지 보여준다.
env:
- name: MY_POD_IPS
valueFrom:
fieldRef:
fieldPath: status.podIPs
다음 커맨드는 컨테이너 내 MY_POD_IPS
환경 변수의 값을 출력한다. 해당 값은 파드의 IPv4 및 IPv6 주소를 나타내는 쉼표로 구분된 목록이다.
kubectl exec -it pod01 -- set | grep MY_POD_IPS
MY_POD_IPS=10.244.1.4,2001:db8::4
파드의 IP 주소는 또한 컨테이너 내 /etc/hosts
에 적힐 것이다. 다음 커맨드는 이중 스택 파드의 /etc/hosts
에 cat을 실행시킨다. 출력 값을 통해 파드의 IPv4 및 IPv6 주소 모두 검증할 수 있다.
kubectl exec -it pod01 -- cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
10.244.1.4 pod01
2001:db8::4 pod01
.spec.ipFamilyPolicy
를 명시적으로 정의하지 않은 다음의 서비스를 생성한다. 쿠버네티스는 처음 구성된 service-cluster-ip-range
에서 서비스에 대한 클러스터 IP를 할당하고 .spec.ipFamilyPolicy
를 SingleStack
으로 설정한다.
apiVersion: v1
kind: Service
metadata:
name: my-service
labels:
app.kubernetes.io/name: MyApp
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
kubectl
을 사용하여 서비스의 YAML을 확인한다.
kubectl get svc my-service -o yaml
이 서비스에서 .spec.ipFamilyPolicy
를 SingleStack
으로 설정하고 .spec.clusterIP
를 kube-controller-manager의 --service-cluster-ip-range
플래그를 통해 설정된 첫 번째 구성 범위에서 IPv4 주소로 설정한다.
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
spec:
clusterIP: 10.0.217.164
clusterIPs:
- 10.0.217.164
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 80
protocol: TCP
targetPort: 9376
selector:
app.kubernetes.io/name: MyApp
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
.spec.ipFamilies
의 첫 번째 배열 요소로 IPv6
을 명시적으로 정의하는 다음 서비스를 생성한다. Kubernetes는 service-cluster-ip-range
로 구성된 IPv6 범위에서 서비스용 클러스터 IP를 할당하고 .spec.ipFamilyPolicy
를 SingleStack
으로 설정한다.
apiVersion: v1
kind: Service
metadata:
name: my-service
labels:
app.kubernetes.io/name: MyApp
spec:
ipFamilies:
- IPv6
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
kubectl
를 사용하여 서비스의 YAML을 확인한다.
kubectl get svc my-service -o yaml
이 서비스에서 .spec.ipFamilyPolicy
를 SingleStack
으로 설정하고 .spec.clusterIP
를 kube-controller-manager의 --service-cluster-ip-range
플래그를 통해 설정된 IPv6 범위에서 IPv6 주소로 설정한다.
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/name: MyApp
name: my-service
spec:
clusterIP: 2001:db8:fd00::5118
clusterIPs:
- 2001:db8:fd00::5118
ipFamilies:
- IPv6
ipFamilyPolicy: SingleStack
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app.kubernetes.io/name: MyApp
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
PreferDualStack
에 .spec.ipFamilyPolicy
을 명시적으로 정의하는 다음 서비스를 생성한다. 쿠버네티스는 IPv4 및 IPv6 주소를 모두 할당하고 (이 클러스터에는 이중 스택을 사용하도록 설정되었으므로) .spec.ipFamilies
배열에 있는 첫 번째 요소의 주소 계열을 기반으로.spec.ClusterIP
목록에서 .spec.ClusterIPs
를 선택한다.
apiVersion: v1
kind: Service
metadata:
name: my-service
labels:
app.kubernetes.io/name: MyApp
spec:
ipFamilyPolicy: PreferDualStack
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
kubectl get svc
명령어는 오직 CLUSTER-IP
필드에 주요 IP만 표시한다.
kubectl get svc -l app.kubernetes.io/name: MyApp
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service ClusterIP 10.0.216.242 <none> 80/TCP 5s
서비스가 kubectl describe
를 사용하여 IPv4 및 IPv6 주소 블록에서 클러스터 IP를 가져오는지 확인한다. 그런 다음 IP 및 포트를 통해 서비스에 대한 접속을 확인할 수 있다.
kubectl describe svc -l app.kubernetes.io/name: MyApp
Name: my-service
Namespace: default
Labels: app.kubernetes.io/name: MyApp
Annotations: <none>
Selector: app.kubernetes.io/name: MyApp
Type: ClusterIP
IP Family Policy: PreferDualStack
IP Families: IPv4,IPv6
IP: 10.0.216.242
IPs: 10.0.216.242,2001:db8:fd00::af55
Port: <unset> 80/TCP
TargetPort: 9376/TCP
Endpoints: <none>
Session Affinity: None
Events: <none>
만약 클라우드 제공자가 IPv6 기반 외부 로드 밸런서 구성을 지원한다면 .spec.ipFamilyPolicy
의 PreferDualStack
과 .spec.ipFamilies
배열의 첫 번째 요소로 IPv6
및 LoadBalancer
로 설정된 type
필드를 사용하여 다음 서비스를 생성한다.
apiVersion: v1
kind: Service
metadata:
name: my-service
labels:
app.kubernetes.io/name: MyApp
spec:
ipFamilyPolicy: PreferDualStack
ipFamilies:
- IPv6
type: LoadBalancer
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
Check the Service:
kubectl get svc -l app.kubernetes.io/name: MyApp
서비스가 IPv6 주소 블록에서 CLUSTER-IP
주소 및 EXTERNAL-IP
주소를 할당받는지 검증한다. 그리고 나서 IP 및 포트로 서비스 접근이 가능한지 검증할 수 있다.
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service LoadBalancer 2001:db8:fd00::7ebc 2603:1030:805::5 80:30790/TCP 35s
Kubernetes v1.26 [stable]
쿠버네티스는 디바이스 플러그인을 사용하여 AMD 및 NVIDIA GPU(그래픽 프로세싱 유닛)를 여러 노드들에 걸쳐 관리하기 위한 안정적인 지원을 포함한다.
이 페이지는 사용자가 GPU를 활용할 수 있는 방법과, 몇 가지 제한 사항에 대하여 설명한다.
쿠버네티스는 디바이스 플러그인을 구현하여 파드가 GPU와 같이 특별한 하드웨어 기능에 접근할 수 있게 한다.
관리자는 해당하는 하드웨어 벤더의 GPU 드라이버를 노드에 설치해야 하며, GPU 벤더가 제공하는 디바이스 플러그인을 실행해야 한다. 다음은 몇몇 벤더의 지침에 대한 웹페이지이다.
플러그인을 한 번 설치하고 나면, 클러스터는 amd.com/gpu
또는 nvidia.com/gpu
를 스케줄 가능한 리소스로써 노출시킨다.
사용자는 이 GPU들을 cpu
나 memory
를 요청하는 방식과 동일하게
GPU 자원을 요청함으로써 컨테이너에서 활용할 수 있다.
그러나 리소스의 요구 사항을 명시하는 방식에
약간의 제약이 있다.
GPU는 limits
섹션에서만 명시되는 것을 가정한다. 그 의미는 다음과 같다.
limits
를 명시할 때 requests
명시하지 않아도 된다.limits
과 requests
를 모두 명시할 수 있지만, 두 값은
동일해야 한다.limits
명시 없이는 GPU requests
를 명시할 수 없다.다음은 GPU를 요청하는 파드에 대한 예제 매니페스트를 보여준다.
apiVersion: v1
kind: Pod
metadata:
name: cuda-vector-add
spec:
restartPolicy: OnFailure
containers:
- name: example-vector-add
image: "registry.example/example-vector-add:v42"
resources:
limits:
gpu-vendor.example/example-gpu: 1 # 1 GPU 요청
만약 클러스터의 노드들이 서로 다른 타입의 GPU를 가지고 있다면, 사용자는 파드를 적합한 노드에 스케줄 하기 위해서 노드 레이블과 노드 셀렉터를 사용할 수 있다.
예를 들면,
# 노드가 가진 가속기 타입에 따라 레이블을 단다.
kubectl label nodes node1 accelerator=example-gpu-x100
kubectl label nodes node2 accelerator=other-gpu-k915
accelerator
레이블 키를 accelerator
로 지정한 것은 그저 예시일 뿐이며,
선호하는 다른 레이블 키를 사용할 수 있다.
만약 AMD GPU 디바이스를 사용하고 있다면, 노드 레이블러를 배치할 수 있다. 노드 레이블러는 GPU 디바이스의 속성에 따라서 노드에 자동으로 레이블을 달아 주는 컨트롤러이다.
현재 이 컨트롤러는 다음의 속성에 대해 레이블을 추가할 수 있다.
kubectl describe node cluster-node-23
Name: cluster-node-23
Roles: <none>
Labels: beta.amd.com/gpu.cu-count.64=1
beta.amd.com/gpu.device-id.6860=1
beta.amd.com/gpu.family.AI=1
beta.amd.com/gpu.simd-count.256=1
beta.amd.com/gpu.vram.16G=1
kubernetes.io/arch=amd64
kubernetes.io/os=linux
kubernetes.io/hostname=cluster-node-23
Annotations: node.alpha.kubernetes.io/ttl: 0
…
노드 레이블러가 사용된 경우, GPU 타입을 파드 스펙에 명시할 수 있다.
apiVersion: v1
kind: Pod
metadata:
name: cuda-vector-add
spec:
restartPolicy: OnFailure
containers:
- name: cuda-vector-add
# https://github.com/kubernetes/kubernetes/blob/v1.7.11/test/images/nvidia-cuda/Dockerfile
image: "registry.k8s.io/cuda-vector-add:v0.1"
resources:
limits:
nvidia.com/gpu: 1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
– matchExpressions:
– key: beta.amd.com/gpu.family.AI # Arctic Islands GPU family
operator: Exist
이것은 사용자가 지정한 GPU 타입을 가진 노드에 파드가 스케줄 되도록 만든다.
Kubernetes v1.31 [stable]
쿠버네티스는 파드의 애플리케이션에 미리 할당된 huge page의 할당과 사용을 지원한다. 이 페이지에서는 사용자가 huge page를 사용하는 방법에 대해 설명한다.
노드는 모든 huge page 리소스를 스케줄 가능한 리소스로 자동 검색하고 보고한다.
리소스 이름 hugepages-<size>
(<size>
는 특정 노드에서 지원되는 정수값을
사용하는 가장 간단한 2진 표기법)를 사용하여 컨테이너 레벨의 리소스
요구 사항을 통해 huge page를 사용할 수 있다. 예를 들어,
노드가 2048KiB 및 1048576KiB 페이지 크기를 지원하는 경우, 스케줄 가능한
리소스인 hugepages-2Mi
와 hugepages-1Gi
를 노출한다. CPU나 메모리와 달리,
huge page는 오버커밋을 지원하지 않는다. 참고로 hugepage 리소스를 요청하는 경우,
메모리 또는 CPU 리소스도 요청해야 한다.
파드는 단일 파드 스펙에 여러 개의 huge page 크기를 사용할 수 있다. 이 경우
모든 볼륨 마운트에 대해 medium: HugePages-<hugepagesize>
표기법을 사용해야 한다.
apiVersion: v1
kind: Pod
metadata:
name: huge-pages-example
spec:
containers:
- name: example
image: fedora:latest
command:
- sleep
- inf
volumeMounts:
- mountPath: /hugepages-2Mi
name: hugepage-2mi
- mountPath: /hugepages-1Gi
name: hugepage-1gi
resources:
limits:
hugepages-2Mi: 100Mi
hugepages-1Gi: 2Gi
memory: 100Mi
requests:
memory: 100Mi
volumes:
- name: hugepage-2mi
emptyDir:
medium: HugePages-2Mi
- name: hugepage-1gi
emptyDir:
medium: HugePages-1Gi
파드는 동일한 크기의 huge page들을 요청하는 경우에만 medium: HugePages
를 사용할 수 있다.
apiVersion: v1
kind: Pod
metadata:
name: huge-pages-example
spec:
containers:
- name: example
image: fedora:latest
command:
- sleep
- inf
volumeMounts:
- mountPath: /hugepages
name: hugepage
resources:
limits:
hugepages-2Mi: 100Mi
memory: 100Mi
requests:
memory: 100Mi
volumes:
- name: hugepage
emptyDir:
medium: HugePages
shmget()
의 SHM_HUGETLB
를 통해 huge page를 사용하는 애플리케이션은
proc/sys/vm/hugetlb_shm_group
과 일치하는 보충 그룹(supplemental group)으로 실행해야 한다.hugepages-<size>
토큰을 사용하는 cpu
또는 memory
와 같은
다른 컴퓨트 리소스와 비슷한 리소스쿼터(ResourceQuota)를 통해 제어할 수
있다.이 가이드는 kubectl 확장을 설치하고 작성하는 방법을 보여준다. 핵심 kubectl
명령을 쿠버네티스 클러스터와 상호 작용하기 위한 필수 구성 요소로 생각함으로써, 클러스터 관리자는
플러그인을 이러한 구성 요소를 활용하여 보다 복잡한 동작을 만드는 수단으로 생각할 수 있다. 플러그인은 새로운 하위 명령으로 kubectl
을 확장하고, 주요 배포판에 포함되지 않은 kubectl
의 새로운 사용자 정의 기능을 허용한다.
동작하는 kubectl
바이너리가 설치되어 있어야 한다.
플러그인은 이름이 kubectl-
로 시작되는 독립형 실행 파일이다. 플러그인을 설치하려면, 실행 파일을 PATH
에 지정된 디렉터리로 옮기면 된다.
Krew를 사용하여 오픈소스에서 사용 가능한 kubectl 플러그인을 검색하고 설치할 수도 있다. Krew는 쿠버네티스 SIG CLI 커뮤니티에서 관리하는 플러그인 관리자이다.
kubectl
은 유효한 플러그인 실행 파일을 PATH
에서 검색하는 kubectl plugin list
명령을 제공한다.
이 명령을 실행하면 PATH
에 있는 모든 파일을 탐색한다. 실행 가능하고, kubectl-
로 시작하는 모든 파일은 이 명령의 출력 결과에 PATH
에 있는 순서대로 표시된다.
실행 파일이 아닌 파일이 kubectl-
시작하는 경우 경고가 포함된다.
서로의 이름과 겹치는 유효한 플러그인 파일에 대한 경고도 포함된다.
Krew를 사용하여 커뮤니티가 관리하는
플러그인 인덱스에서 kubectl
플러그인을 검색하고 설치할 수 있다.
현재 기존 kubectl
명령을 덮어 쓰는 플러그인을 생성할 수 없다. 예를 들어, 플러그인 kubectl-version
을 만들면 기존의 kubectl version
명령이 항상 우선하므로, 플러그인이 실행되지 않는다. 이 제한으로 인해, 플러그인을 사용하여 기존 kubectl
명령에 새로운 하위 명령을 추가할 수도 없다. 예를 들어, 플러그인 이름을 kubectl-create-foo
로 지정하여 kubectl create foo
하위 명령을 추가하면 해당 플러그인이 무시된다.
kubectl plugin list
는 이를 시도하는 유효한 플러그인에 대한 경고를 표시한다.
커맨드-라인 명령을 작성할 수 있는 프로그래밍 언어나 스크립트로 플러그인을 작성할 수 있다.
플러그인 설치 또는 사전 로딩이 필요하지 않다. 플러그인 실행 파일은
kubectl
바이너리에서 상속된 환경을 받는다.
플러그인은 이름을 기반으로 구현할 명령 경로를 결정한다.
예를 들어, kubectl-foo
라는 플러그인은 kubectl foo
명령을 제공한다.
PATH
어딘가에 플러그인 실행 파일을 설치해야 한다.
#!/bin/bash
# 선택적 인수 처리
if [[ "$1" == "version" ]]
then
echo "1.0.0"
exit 0
fi
# 선택적 인수 처리
if [[ "$1" == "config" ]]
then
echo "$KUBECONFIG"
exit 0
fi
echo "I am a plugin named kubectl-foo"
플러그인을 사용하려면, 실행 가능하게 만든다.
sudo chmod +x ./kubectl-foo
그리고 PATH
의 어느 곳에나 옮겨 놓는다.
sudo mv ./kubectl-foo /usr/local/bin
이제 플러그인을 kubectl
명령으로 호출할 수 있다.
kubectl foo
I am a plugin named kubectl-foo
모든 인수와 플래그는 그대로 실행 파일로 전달된다.
kubectl foo version
1.0.0
모든 환경 변수도 실행 파일로 그대로 전달된다.
export KUBECONFIG=~/.kube/config
kubectl foo config
/home/<user>/.kube/config
KUBECONFIG=/etc/kube/config kubectl foo config
/etc/kube/config
또한, 플러그인으로 전달되는 첫 번째 인수는 항상 호출된 위치의 전체 경로이다(위의 예에서 $0
은 /usr/local/bin/kubectl-foo
와 동일하다).
위 예제에서 볼 수 있듯이, 플러그인은 파일명을 기반으로 구현할 명령 경로를 결정한다. 플러그인이 대상으로 하는 명령 경로의 모든 하위 명령은 대시(-
)로 구분된다.
예를 들어, 사용자가 kubectl foo bar baz
명령을 호출할 때마다 호출되는 플러그인은 파일명이 kubectl-foo-bar-baz
이다.
플러그인 메커니즘은 플러그인 프로세스에 대한 사용자 정의, 플러그인 특정 값 또는 환경 변수를 생성하지 않는다.
이전 kubectl 플러그인 메커니즘은 KUBECTL_PLUGINS_CURRENT_NAMESPACE
와 같이 더이상 사용하지 않는 환경 변수를 제공했다.
kubectl 플러그인은 전달된 모든 인수를 파싱하고 유효성을 검사해야 한다. 플러그인 작성자를 대상으로 하는 Go 라이브러리에 대한 자세한 내용은 커맨드 라인 런타임 패키지 사용을 참고한다.
다음은 사용자가 추가 플래그와 인수를 제공하면서 플러그인을 호출하는 추가 사례이다. 이것은 위 시나리오의 kubectl-foo-bar-baz
플러그인을 기반으로한다.
kubectl foo bar baz arg1 --flag=value arg2
를 실행하면, kubectl의 플러그인 메커니즘은 먼저 가장 긴 가능한 이름을 가진 플러그인을 찾으려고 시도한다. 여기서는
kubectl-foo-bar-baz-arg1
이다. 해당 플러그인을 찾지 못하면, kubectl은 대시로 구분된 마지막 값을 인수(여기서는 arg1
)로 취급하고, 다음으로 가장 긴 가능한 이름인 kubectl-foo-bar-baz
를 찾는다.
이 이름의 플러그인을 찾으면, kubectl은 해당 플러그인을 호출하여, 플러그인 이름 뒤에 모든 인수와 플래그를 플러그인 프로세스의 인수로 전달한다.
예:
# 플러그인 생성
echo -e '#!/bin/bash\n\necho "My first command-line argument was $1"' > kubectl-foo-bar-baz
sudo chmod +x ./kubectl-foo-bar-baz
# $PATH에 있는 디렉터리로 옮겨 플러그인을 "설치"
sudo mv ./kubectl-foo-bar-baz /usr/local/bin
# 플러그인을 kubectl이 인식하는지 확인
kubectl plugin list
The following kubectl-compatible plugins are available:
/usr/local/bin/kubectl-foo-bar-baz
# test that calling your plugin via a "kubectl" command works
# even when additional arguments and flags are passed to your
# plugin executable by the user.
kubectl foo bar baz arg1 --meaningless-flag=true
My first command-line argument was arg1
보시다시피, 사용자가 지정한 kubectl
명령을 기반으로 플러그인을 찾았으며, 모든 추가 인수와 플래그는 플러그인 실행 파일이 발견되면 그대로 전달된다.
kubectl
플러그인 메커니즘은 플러그인 파일명에 대시(-
)를 사용하여 플러그인이 처리하는 하위 명령 시퀀스를 분리하지만, 파일명에
언더스코어(_
)를 사용하여 커맨드 라인 호출에 대시를 포함하는 플러그인 명령을 생성할 수 있다.
예:
# 파일명에 언더스코어(_)가 있는 플러그인 생성
echo -e '#!/bin/bash\n\necho "I am a plugin with a dash in my name"' > ./kubectl-foo_bar
sudo chmod +x ./kubectl-foo_bar
# $PATH에 플러그인을 옮긴다
sudo mv ./kubectl-foo_bar /usr/local/bin
# 이제 kubectl을 통해 플러그인을 사용할 수 있다
kubectl foo-bar
I am a plugin with a dash in my name
참고로 플러그인 파일명에 언더스코어를 추가해도 kubectl foo_bar
와 같은 명령을 사용할 수 있다.
위 예에서 명령은 대시(-
) 또는 언더스코어(_
)을 사용하여 호출할 수 있다.
# 대시를 포함한 사용자 정의 명령을 사용할 수 있다
kubectl foo-bar
I am a plugin with a dash in my name
# 언더스코어를 포함한 사용자 정의 명령을 사용할 수도 있다
kubectl foo_bar
I am a plugin with a dash in my name
PATH
의 다른 위치에 동일한 파일명을 가진 여러 플러그인이 있을 수 있다.
예를 들어, PATH
값이 PATH=/usr/local/bin/plugins:/usr/local/bin/moreplugins
로 주어지고, kubectl-foo
플러그인을 복사한 파일이 /usr/local/bin/plugins
와 /usr/local/bin/moreplugins
에 있을 수 있다.
kubectl plugin list
명령의 출력 결과는 다음과 같다.
PATH=/usr/local/bin/plugins:/usr/local/bin/moreplugins kubectl plugin list
The following kubectl-compatible plugins are available:
/usr/local/bin/plugins/kubectl-foo
/usr/local/bin/moreplugins/kubectl-foo
- warning: /usr/local/bin/moreplugins/kubectl-foo is overshadowed by a similarly named plugin: /usr/local/bin/plugins/kubectl-foo
error: one plugin warning was found
위 시나리오에서, /usr/local/bin/moreplugins/kubectl-foo
아래의 경고는 이 플러그인이 실행되지 않을 것임을 알려준다. 대신, PATH
에 먼저 나타나는 실행 파일인 /usr/local/bin/plugins/kubectl-foo
는 항상 발견되고 kubectl
플러그인 메카니즘에 의해 먼저 실행된다.
이 문제를 해결하는 방법은 kubectl
와 함께 사용하려는 플러그인의 위치가 PATH
에 항상에서 먼저 오도록 하는 것이다. 예를 들어, kubectl
명령 kubectl foo
가 호출될 때마다 항상 /usr/local/bin/moreplugins/kubectl-foo
를 사용하려면, PATH
의 값을 /usr/local/bin/moreplugins:/usr/local/bin/plugins
로 변경한다.
플러그인 파일명으로 발생할 수 있는 또 다른 종류의 오버셰도잉이 있다. 사용자의 PATH
에 kubectl-foo-bar
와 kubectl-foo-bar-baz
라는 두 개의 플러그인이 있다면, kubectl
플러그인 메커니즘은 항상 주어진 사용자의 명령에 대해 가장 긴 가능한 플러그인 이름을 선택한다. 아래에 몇 가지 예가 있다.
# 주어진 kubectl 명령의 경우, 가장 긴 가능한 파일명을 가진 플러그인이 항상 선호된다
kubectl foo bar baz
Plugin kubectl-foo-bar-baz is executed
kubectl foo bar
Plugin kubectl-foo-bar is executed
kubectl foo bar baz buz
Plugin kubectl-foo-bar-baz is executed, with "buz" as its first argument
kubectl foo bar buz
Plugin kubectl-foo-bar is executed, with "buz" as its first argument
이 디자인 선택은 필요한 경우 여러 파일에 플러그인 하위 명령을 구현할 수 있도록 하고 이러한 하위 명령을 "부모" 플러그인 명령 아래에 중첩할 수 있도록 한다.
ls ./plugin_command_tree
kubectl-parent
kubectl-parent-subcommand
kubectl-parent-subcommand-subsubcommand
위에서 언급한 kubectl plugin list
명령을 사용하여 kubectl
에 의해 플러그인이 표시되는지 확인하고, kubectl
명령으로 호출되지 못하게 하는 경고가 없는지 확인할 수 있다.
kubectl plugin list
The following kubectl-compatible plugins are available:
test/fixtures/pkg/kubectl/plugins/kubectl-foo
/usr/local/bin/kubectl-foo
- warning: /usr/local/bin/kubectl-foo is overshadowed by a similarly named plugin: test/fixtures/pkg/kubectl/plugins/kubectl-foo
plugins/kubectl-invalid
- warning: plugins/kubectl-invalid identified as a kubectl plugin, but it is not executable
error: 2 plugin warnings were found
kubectl 용 플러그인을 작성하고 있고 Go를 사용한다면, cli-runtime 유틸리티 라이브러리를 사용할 수 있다.
이 라이브러리는 사용자의 kubeconfig 파일을 파싱이나 업데이트하거나, REST 스타일의 요청을 API 서버에 작성하거나, 구성 및 출력과 관련된 플래그를 바인딩하기 위한 헬퍼를 제공한다.
CLI 런타임 리포지터리에 제공된 도구 사용법의 예제는 샘플 CLI 플러그인을 참고한다.
다른 사람이 사용할 수 있는 플러그인을 개발한 경우, 이를 패키징하고, 배포하고 사용자에게 업데이트를 제공하는 방법을 고려해야 한다.
Krew는 플러그인을 패키징하고 배포하는 크로스-플랫폼 방식을 제공한다. 이렇게 하면, 모든 대상 플랫폼(리눅스, 윈도우, macOS 등)에 단일 패키징 형식을 사용하고 사용자에게 업데이트를 제공한다. Krew는 또한 다른 사람들이 여러분의 플러그인을 검색하고 설치할 수 있도록 플러그인 인덱스를 유지 관리한다.
다른 방법으로는, 리눅스의 apt
나 yum
,
윈도우의 Chocolatey, macOS의 Homebrew와 같은 전통적인 패키지 관리자를 사용할 수 있다.
새 실행 파일을 사용자의 PATH
어딘가에 배치할 수 있는 패키지 관리자라면
어떤 패키지 관리자도 괜찮다.
플러그인 작성자로서, 이 옵션을 선택하면 각 릴리스의 여러 플랫폼에서
kubectl 플러그인의 배포 패키지를
업데이트해야 한다.
소스 코드를 게시(예를 들어, Git 리포지터리)할 수 있다. 이 옵션을 선택하면, 해당 플러그인을 사용하려는 사람이 코드를 가져와서, 빌드 환경을 설정하고(컴파일이 필요한 경우), 플러그인을 배포해야 한다. 컴파일된 패키지를 사용 가능하게 하거나, Krew를 사용하면 설치가 더 쉬워진다.