반응형

 



설치 및 테스트한 날짜: 2023년 4월 12일
Harbor version: v2.7.0

 

아래 문서에 준비 작업, 설정, 설치 과정이 자세히 설명되어 있다. 세부적인 설정이 필요하다면 아래 문서를 읽어보는 것이 좋다.

 

https://goharbor.io/docs/2.7.0/install-config/

 

Harbor – Harbor Installation and Configuration

Harbor Installation and Configuration

goharbor.io

 

 

1) 사전 준비 작업

아래 Web docs에 Harbor 운영에 필요한 HW 스펙 및 Software, Network port 정보가 있다.

 

https://goharbor.io/docs/2.7.0/install-config/installation-prereqs/

 

특별한 내용이 없으니까, 간단하게 읽고 패쑤~

 

 

2) Harbor Installer 다운로드 받기

아래 Web docs에 Harbor installer를 다운로드할 수 있는 절차가 설명되어 있다.

특별한 내용이 없으니까, 설명되어 있는 명령을 따라서 수행하면 된다.

 

  - 다운로드에 대한 설명 : https://goharbor.io/docs/2.7.0/install-config/download-installer/

  - 설치 파일 저장소:  https://github.com/goharbor/harbor/releases/tag/v2.7.0  (이 페이지의 제일 밑에 있는 링크를 사용)

  - 실제 다운로드한 파일: https://github.com/goharbor/harbor/releases/download/v2.7.0/harbor-offline-installer-v2.7.0.tgz

 

나는 여러 설치 파일 중에서 'harbor-offline-installer-v2.7.0.tgz' 파일을 다운로드 받았다.

그리고 이 tgz 압축 파일을 풀어준다.  (파일을 설정하고 실행하는 것은 뒤에서 다시 설명함)

 

3) Harbor 서버에 HTTPS Access하기 위한 인증서 만들기

OpenSSL 명령 도구를 잘 사용하는 사람이라면, 아래 Web docs의 예제 명령어를 조금 수정해서 수행하면 된다.

그런데 SSL 지식이 없거나 OpenSSL 명령 도구가 생소한 사람이라면, SSL을 공부하고 아래 Web docs 예제를 따라가는 것이 좋다.

 

https://goharbor.io/docs/2.7.0/install-config/configure-https/

 

내 경우, 위 문서의 예제 명령에서 "yourdomain.com" 부분만 실제 나의 internet domain name으로 변경하고 인증서를 만들었다.

 

 

4) 설정 파일 harbor.yaml 작성하기

위에서 다운로드한 'harbor-offline-installer-v2.7.0.tgz' 파일에 harbor.yml.tmpl 파일이 있다.

이 설정 파일을 내 상황에 맞게 편집하면 되는데, 솔직히 항목이 많고 설명을 읽어도 아리송한 부분이 있다.

내 경우는 전체 설정 항목 중에서 아래 예시와 같이 몇 개만 수정했다.

 

##
## 설정 Template 파일을 복사
##

$  cp  harbor.yml.tmpl  harbor.yml



##
## 설정 파일을 편집하기
##

$  vi  harbor.yml

... 중간 생략 ...

hostname: registry.sejong-world.kr

... 중간 생략 ...

https:
  port: 443
  certificate: /data/cert/sejong-world.kr.crt
  private_key: /data/cert/sejong-world.kr.key

harbor_admin_password: MyNewPass56789


$

 

 

 

5) 설치 Script 실행하기

아래 Web docs에 설명이 잘 되어 있어서 따라 하기만 해도 잘 설치된다.

 

https://goharbor.io/docs/2.7.0/install-config/run-installer-script/

 

단, 설치하는 방식에 따라 여러 옵션을 사용할 수 있는데 나는 Chart repository 서비스도 사용할 거라서

아래와 같이  --with-chartmuseum 옵션을 추가했다.

이 옵션으로 설치하면, container image registry와 chart repository 서비스를 모두 사용할 수 있다.

 

$ sudo ./install.sh --with-chartmuseum

 

 

위 옵션을 지정한 것 외에는 나머지 모든 설치 및 기동 종료 절차는 Web docs와 완전히 동일하게 수행했다.

문서의 설명이 간단했고, 따라만 해도 에러없이 설치가 술술 잘 되어서 부연 설명할 부분이 없다.

 

##
## Harbor 서버 종료하기  
##  (옵션 '-v'는 데이터 볼륨을 삭제하라는 뜻)
##

$  docker compose down -v


##
## Harbor 서버 시작하기 
##  (옵션 '-d'는 백그라운드에서 데몬 프로세스로 구동하라는 뜻)
##
$ docker compose up -d

 

 

 

 

 

 

 


 

반응형

 


 

Kubernetes 또는 Docker, CRI-O 등 Container runtime 환경에서 container를 사용하다보면,

UTC + 0로 timezone이 설정되어 있는 경우를 자주 본다.

Container 내부의 timezoneSeoul(서울)로 변경하려면 아래와 같이 따라하면 된다.

 

FROM ubuntu:22.04

... 중간 생략 ...

##
## Timezone 변경: Asia/Seoul 로 설정
##

RUN   apt install -y tzdata
RUN   ln -snf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

... 중간 생략 ...

 


 

 

반응형

 

Kubernetes Cluster를 설치한 날짜: 2022년 12월 21일

 


참고:
한땀 한땀 설치 과정을 이해하고 테스트하면서 kubernetes 내부 구성을 스터디하는 것이 목적이 아니라면,
아래 블로그를 읽고 kubespray로 설치하는 것을 추천한다.
  https://andrewpage.tistory.com/305

https://andrewpage.tistory.com/305

 

 

 

 

Kubernetes cluster(쿠버네티스 클러스터) 구축을 도와주는 여러 도구(kubeadm, kubespray, kops)가 있다.

이 문서에는 그런 kubernetes cluster 구축 도구 중에서 kubeadm을 이용하여 kubernetes cluster를 구축하는 방법을 설명한다.

 

참고로, kubeadm 도구가 가장 수작업이 많고 사람 손을 많이 탄다. 

그렇지만 Kubernetes 내부 구성을 이해하고 싶거나 작동 원리를 알고 싶다면 kubeadm 도구를 이용해서 설치하면 좋다.

즉, kubernetes를 공부하는 것이 목적이라면 kubeadm 관리 도구를 사용하는 것을 추천한다.

 

 

Kubernetes Cluster 구축을 위한 계획하기

Ubuntu 22.04 를 설치한 Master node와 Worker node를 준비한다.

  • Master Node: 1개 (master-0)
  • Worker Node: 2개 (worker-0, worker-1)

 

Master Node와 Worker Node 준비 작업

master node와 worker node 모두에서 아래의 작업을 수행한다.

kubernetes는 iptables를 이용하여 pod간 통신을 가능하게 한다. 따라서 iptables가 정상 동작하도록 하기 위해 아래와 같이 설정한다.

 

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# sysctl params required by setup, params persist across reboots
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF

# Apply sysctl params without reboot
sudo sysctl --system

 

모든 master node, worker node에서 swap 영역을 비활성화한다.

 

$  sudo sed -i '/swap/d' /etc/fstab

##
## 또는 위와 명령 대신, /etc/fstab 파일을 열어서 swap 과 관련있는 filesystem 항목을
## 주석으로 막아도 된다.
##

$  sudo swapoff -a

$  free -h
              total        used        free      shared  buff/cache   available
Mem:            15G        1.0G         13G         13M        925M         14G
Swap:            0B          0B          0B
$

 

방화벽(Firewalld)를 비활성화한다.

(원칙은 kubernetes가 사용하는 service port만 allow 설정해야 하지만, 여기서는 간단하게 firewalld를 종료하는 것으로 하겠다)

$  systemctl stop firewalld
$  systemctl disable firewalld

 

 

Container Runtime Interface(CRI) 설치하기 (여기서는 CRI-O를 설치!)

master node와 worker node 모두에서 아래의 작업을 수행한다.

$ sudo -s

$ apt update && sudo apt upgrade

$ OS=xUbuntu_22.04

##
## 주의: Kubernetes 1.25를 설치할 것이기 때문에
##      CRIO도 1.25를 설치하는 것이다. (즉, 2개의 버전이 일치해야 한다)
##

$ CRIO_VERSION=1.25

$ echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/ /"|sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list

$ echo "deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$CRIO_VERSION/$OS/ /"|sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:$CRIO_VERSION.list

$ curl -L https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:$CRIO_VERSION/$OS/Release.key | sudo apt-key add -

$ curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/Release.key | sudo apt-key add -

$ sudo apt update

$ sudo apt install cri-o cri-o-runc

$ apt show cri-o

$ systemctl enable --now crio.service

$ systemctl status crio

$ apt install cri-tools

$ crictl info

##
## cri-o가 잘 동작하는지 확인하기 위해 아래와 같이 container image를 pulling하는 테스트한다.
##

$ crictl pull busybox

$ crictl images

 

 

 

 

 

 

 


여기까지 설명이  master node, worker node에서 준비해야 할 작업이다.
이 다음 설명부터 실제 kubernetes cluster를 구축하기 위한 작업이다.

 

 


 

kubeadm, kubelet, kubectl 설치

kubeadm: kubernetes cluster를 구축하기 위한 명령 도구

kubelet: master node, worker node에서 데몬 프로세스로 기동되어 있으면서, container와 pod를 생성/삭제/상태를 감시한다.

kubectl: 사용자가 kubernetes cluster에게 작업 요청하기 위한 명령 도구 (예를 들어, 'pod를 생성해달라!'  'pod의 개수를 늘려달라!' 같은 사용자 명령을 kunernetes API server에게 전달)

 

아래의 명령을 따라 하여 kubeadm, kubelet, kubectl 명령 도구를 설치한다.

(모든 장비에서 수행해야 한다. 즉, Master Node와 Worker Node 모두에 해당하는 작업이다.)

 

$ sudo -s

##
## Update the apt package index and 
## install packages needed to use the Kubernetes apt repository:
## 

$ apt-get update

$ sudo apt-get install -y apt-transport-https ca-certificates curl


##
## Download the Google Cloud public signing key:
## 

$ curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg

##
## Add the Kubernetes apt repository:
##

$ echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list

##
## Update apt package index, install kubelet, kubeadm and kubectl, and pin their version:
##

$ apt-get update

$ apt-get install -y kubelet kubeadm kubectl

 

 

Kubernetes Cluster 생성하기

아래 명령을 master-0 node에서 수행한다.

 

##
## 주의: Master-0 노드에서만 수행해야 한다.
##

$ sudo -s

$  kubeadm init --apiserver-advertise-address 10.10.1.10
...
...


##
## Cluster 초기화는 2~3분 정도 걸린다.
## 초기화 작업이 끝나면 아래의 명령을 수행한다.
##

$ mkdir -p $HOME/.kube

$ cp -i /etc/kubernetes/admin.conf $HOME/.kube/config

$ chown $(id -u):$(id -g) $HOME/.kube/config

 

위 명령을 수행하고 바로 Container Network Interface(CNI) 설치하는 작업을 수행해야 한다.

 

 

CNI 설치

CNI에는 많은 종류가 있지만, 사용하기 쉽고 대중적인 Calico 또는 Flannel CNI를 설치하겠다.

(Case A)  Calico를 설치하는 경우  <-- 추천

## 참고: 2023년 4월 23일 현재, v3.25.1을 설치하는 것을 권장.
$ kubectl apply  -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.1/manifests/calico.yaml

##
## 주의: 위 명령을 수행하고 나서, `kubectl get pod -A` 명령으로 calico 관련 pod가 모두 기동했는지
##      확인한 후에 다음 절차를 수행해야 한다.
##

(Case B)  Flannel을 설치하는 경우 <-- 나는 개인적으로 별루~  (몇 달 운영하다가 Error  발생한 경험이 있어서)

$  kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

 

 

※ 참고

위 명령 `kubectl apply -f .......calico.yaml` 을 수행 후 CNI 설치에 문제가 있었다면, Pod 'coredns-xxxxxx'  구동하지 못하고 'pending' 상태로 남게 된다.

그런 경우는 대부분 `kubeadm init --pod-network-cidr=10.244.0.0/16` 명령을 수행했을 때 사용했던 CIDR 값이 master node, worker node의 물리 주소와 겹치는 경우에 문제가 발생한다. 따라서 '10.244.0.0/16' 값이 아닌 다른 값으로 다시 변경해서 kubernetes cluster를 생성해보면 문제가 해결될 수 있다.

 

 

잠깐:
  위 명령을 수행하고 대략 3분 쯤 지난 후, 아래와 같이 Pod의 기동 상태를 확인하자!
  모든 Pod가 Running 상태로 변경된 이후에 나머지 작업을 진행한다.
root@master-a:~# kubectl get pod -A
NAMESPACE     NAME                                       READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-74677b4c5f-88gzk   1/1     Running   0          55s
kube-system   calico-node-g2dcp                          1/1     Running   0          55s
kube-system   coredns-565d847f94-8mgrs                   1/1     Running   0          3m9s
kube-system   coredns-565d847f94-gsd5q                   1/1     Running   0          3m9s
kube-system   etcd-master-a                              1/1     Running   0          3m16s
kube-system   kube-apiserver-master-a                    1/1     Running   0          3m15s
kube-system   kube-controller-manager-master-a           1/1     Running   0          3m14s
kube-system   kube-proxy-vbg6h                           1/1     Running   0          3m10s
kube-system   kube-scheduler-master-a                    1/1     Running   0          3m15s
root@master-a:~#

 

 

Worker node joining

위에서 `kubeadm init` 명령을 수행했을 때, 출력되었던 메시지 중에 `kubeadm join ....` 과 같은 형태의 메시지가 있었을 것이다.

그 메시지를 복사해서  모든 worker node에서 수행한다.

# worker-0, worker-1에서 아래의 명령을 수행한다.
kubeadm join 10.1.3.170:6443 --token pdfjgjas.psdfjbh kajsdjhasdfv \
    --discovery-token-ca-cert-hash sha256:3nasklj46kj234k5lj12k3j4gkfdjjgdsfh51a3a686

위 명령이 수행된 이후에 master-0 node에서 아래의 명령으로 cluster 구축된 결과를 확인한다.

$  kubectl get node
NAME                         STATUS   ROLES                  AGE    VERSION
master-0.kube.sejong.space   Ready    control-plane,master   3m     v1.25.2
worker-0.kube.sejong.space   Ready    <none>                 1m     v1.25.2
worker-1.kube.sejong.space   Ready    <none>                 1m     v1.25.2

 

Kubernetes cluster 삭제 (Tear down)

만약 깔끔하게 kubernetes cluster를 지우고, 처음부터 다시 구축하고 싶다면 아래와 같이 cluster를 reset 한다.

$  apt install ipvsadm

$  kubeadm reset;   rm -rf  $HOME/.kube  /etc/kubernetes;   rm -rf /etc/cni;  ipvsadm --clear

 

 

Bastion Node 설정

위 설명에서는 kubectl 명령을 master-0 node에서 수행했다.

그러나 일반적으로 master-0에 직접 SSH 접속해서 kubectl 명령을 수행하는 것을 권장하지 않는다.

kubernetes cluster node는 운영 node이기 때문에 개발자가 접속하는 것이 바람직하지 않다.

(어쩌면, 보안 규정상 개발자가 master node에 SSH 접속하는 것 자체를 허용하지 않는 회사도 있을 것이다)

따라서 master-0 node가 아닌 본인의 PC(예를 들어 MacBook 같은 PC)에서 접속하는 방법을 사용하는 것을 권장한다.

방법은 간단하다.

master-0 node에 있는 /etc/kubernetes/admin.conf 파일을 내 PC(예를 들어 Macbook)에 복사하기만 하면 된다.

# MacOS를 사용한다고 가정하고 설명하겠다.
$  mkdir -p $HOME/.kube
$  cd $HOME/.kube
$  master-0 node의 '/etc/kubernetes/admin.conf' 파일을 내 PC로 내려받는다.
$  mv  admin.conf  config

# 내 PC에서 Kubernetes cluster의 API 서버로 잘 접속하는지 아래와 같이 명령을 수행해본다.
$  kubectl get node
NAME                         STATUS   ROLES                  AGE    VERSION
master-0.kube.sejong.space   Ready    control-plane,master   128m   v1.25.2
worker-0.kube.sejong.space   Ready    <none>                 126m   v1.25.2
worker-1.kube.sejong.space   Ready    <none>                 126m   v1.25.2
$

 


 

이 아래 부분에서 설명하는 작업 절차는 Kubernetes를 운영하는 데 있어서 꼭 필요한 것은 아니고, Web Dashboard로 좀 더 예쁘게 Kubernetes cluster를 모니터링하고 싶은 경우에 아래 Web Dashboard 설정 작업을 해주면 좋다.

 

Kubernetes Web Dashboard 설치 및 설정

내가 참고했던 Web docs(https://waspro.tistory.com/516) 가 있고, 이 문서에서 설명한 3가지 방식 중에서 3번째 방식(Kubernetes API Server 연동 방식)을 사용하는 것을 권장한다.

이 Web Docs에 설명이 잘 되어 있어서 내가 별도 설명할 필요 없을 것이고, 내가 수행했던 명령만 로그로 남겨보겠다.

 

$  kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta8/aio/deploy/recommended.yaml
$  kubectl proxy   &

# Service Account 생성

$  cat <<EOF | kubectl create -f -
 apiVersion: v1
 kind: ServiceAccount
 metadata:
   name: admin-user
   namespace: kube-system
EOF
$

# ClusterRoleBinding을 생성

$  cat <<EOF | kubectl create -f -
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRoleBinding
 metadata:
   name: admin-user
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: ClusterRole
   name: cluster-admin
 subjects:
 - kind: ServiceAccount
   name: admin-user
   namespace: kube-system
EOF
$

# 사용자 계정의 Token 확인
$  kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}') 
Name:         admin-user-token-p9ldd
Namespace:    kube-system
Labels:       <none>
Annotations:  kubernetes.io/service-account.name=admin-user
              kubernetes.io/service-account.uid=041cb7ec-946a-49b6-8900-6dc90fc08464

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1025 bytes
namespace:  11 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6InFmdGVUUnB6QmhoOHhlSDBJLUNLVHlEWWxpd2ZVaDhBVjZOQXE5TElhVWsifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLWZ2dG5uIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJjOGExZTY3MS00NmY1LTQwZjctODNkYy02YTE4N2NiYzkzYmYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06YWRtaW4tdXNlciJ9.lKKD4yEvlpFJ7-BNuPTyO3YRuFYYdQMgPX5azXwn4bZiki2Y886k1dnNM16L4YuA_SSahrPtitSzPfevlAoeC5msdDg1DKRdFuGuYkkI_u_KvOv7orMopDDgZs0zuXFrHIZa1-qiWbgvHfgokOMvndchCcMHyo8pKD3vdBAq_AxtGlCIPImkfALM_d41FrBKIXEjdoCcHkPu7Cz13UAxNRBRs-d274g2UNz-MUnNiomDhJlcYFXTXeooKjHhUiyoFLCgP-V6Wh_1QSCwdfYZGQ1bF0QcZINQZdwluyOtP43AjXHxdSBrAGIPfaY7qsBR_b2upuUDnQsA1w7qkaQB0g     <== 이 빨간색 token을 Web dashboard login 화면에 붙여 넣고, "Sign-in" 버튼을 누른다.
$

 

위와 같이 ServiceAccount와 ClusterRole을 설정하고, secret을 생성/등록한 후에 Web Browser에서 접속하면 된다.

 

접속 주소 예시:  http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

 

 

 

 


 

Troubleshooting & How to clear the issue

kubeadm join  명령이 실패하는 경우.

대부분 master node에서 생성한지 1시간이 초과된 token 값을 이용해서 worker node에서 join하려고 하면 

'kubeadm join' 명령이 실패한다.

worker node 1, 2, ... 9 이런 식으로 순차적으로 작업하다가 보면, 거의 끝 부분에 있는 worker node 9는 이미 1 시간이 지난 뒤에

'kubeadm join'을 하게 되므로 종종 실패하게 된다.

그러나 심각한 문제는 아니고, master node에서 'kubeadm token create ...' 명령을 사용해서 다시 token 값을 생성해주기만 하면 된다.

아래와 같이 master node에서 token create하고, worker node에서 새로 만들어진 token 값으로 `kubeadm join'하면 된다.

##
## On master node.
##
$ kubeadm token create --print-join-command

kubeadm join 10.10.3.33:6443 --token z53s7g.aa...zc --discovery-token-ca-cert-hash sha256:372...3a686


##
## On worker node.
##
$ kubeadm join 10.10.3.33:6443 --token z53s7g.aa...zc --discovery-token-ca-cert-hash sha256:372...3a686

[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

$


##
## 위와 같이 worker node의 joining이 성공하면, 
## 그 동안 activating (auto-restart) 상태였던 kubelet에 아래와 같이 active(running) 상태로 바뀐다.
## On worker node.
##

$ systemctl status kubelet

● kubelet.service - kubelet: The Kubernetes Node Agent
   Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; vendor preset: disabled)
  Drop-In: /usr/lib/systemd/system/kubelet.service.d
           └─10-kubeadm.conf
   Active: active (running) since Thu 2021-11-11 15:38:54 KST; 17s ago
   ...
   ...
   
 $

위와 같이 Active: active (running) 상태로 출력되면, 정상적으로 kubelet이 기동되고 Master node와 연동된 것이다.

 

게시물 작성자: sejong.jeonjo@gmail.com

 

 

반응형

Kubernetes, OCP(Openshift Container Platform)을 사용하다 보면, Resource에 대한 접근 권한 때문에 답답한 경우가 많다.

SCC(SecurityContext)를 잘 관리할 줄 알면, 제일 좋지만 급하게.. 그리고 간단하게 테스트만 하는 상황이라면,

아래와 같이 시스템의 모든 자원에 접근할 수 있도록 권한을 최고 수준(privileged)으로 올려주고 테스트하는 것이 정신 건강에 좋다.

(상용 서비스에서는 절대 이렇게 하면 안 된다.  Test bed 같은 곳에서 잠깐 Feasibility check만 하고 Pod를 종료시킨다는 가정하에 사용할 것)

 

apiVersion: v1
kind: Pod
metadata:
  name: my-example-pod
spec:
  hostNetwork: true    ## <-- 이 설정이 있으면, Pod 내부에서 Host Network이 보인다
                       ##     예를 들어, Host OS의 eno3 network port를 보기 위해
                       ##     ifconfit eno3  명령 실행이 가능해진다.
  containers:
    ...
    securityContext:
      privileged: true   ## <-- 이렇게 하면 모든 자원에 접근 가능해진다.
    ...
  securityContext: {}
  ...

 

 

만약, 극단적으로 Pod 내부에 있는 Process가 Host OS의 자원에 제약 없이 접근하고 싶다면 아래 예제 YAML처럼 권한 설정을 하면 된다.  이렇게 하면, Pod 내부에서 Pod 밖(즉 Host OS)의 IP network 자원, TCP Stack, IPC, ProcessID(PID) 등에 자유롭게 접근할 수 있다.

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: privileged
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
spec:
  privileged: true
  allowPrivilegeEscalation: true
  allowedCapabilities:
  - '*'
  volumes:
  - '*'
  hostNetwork: true
  hostPorts:
  - min: 0
    max: 65535
  hostIPC: true
  hostPID: true
  runAsUser:
    rule: 'RunAsAny'
  seLinux:
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'RunAsAny'
  fsGroup:
    rule: 'RunAsAny'

 

위 YAML에 대해 자세히 알고 싶다면, 아래 kubernetes.io 문서를 참고하세요.

 

 

Pod Security Policies

FEATURE STATE: Kubernetes v1.21 [deprecated] Caution: PodSecurityPolicy is deprecated as of Kubernetes v1.21, and will be removed in v1.25. We recommend migrating to Pod Security Admission, or a 3rd party admission plugin. For a migration guide, see Migrat

kubernetes.io

 

 

 

 

다시 한번 강조하지만, 위와 같이 securityContext.privileged: true 설정된 Pod를 상용 서비스를 제공하는 클러스터에서 구동한 상태로 오래 방치하면 결국 해커의 먹이감이 된다.  꼭 10분~20분 정도 테스트만 하고 바로 Pod를 종료(삭제)해야 한다.

 

 

 


 

 

 


아래 내용은 참고용으로만 볼 것!!!

 

DaemonSet으로 구동하는 Pod들은 securityContext 값이 privileged 으로 설정된 경우가 많다.

왜냐하면 이런 DaemonSet으로 구동된 Pod들은 각 노드에서 Host 자원에 접근해야 하는 경우가 있기 때문이다.

아래 예시를 보면 바로 이해가 될것이다.

 

Multus 배포 예시

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: multus
  namespace: openshift-multus
  ... 중간 생략 ...
  
  spec:
    template:
    ... 중간 생략 ...
    
    spec:
      containers:
      ... 중간 생략 ...
      
        securityContext:
          privileged: true      ## <-- 이 부분
... 중간 생략 ...

 

 

Openshift SDN 배포 예시

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: sdn
  ... 중간 생략 ...
spec:
  template:
  ... 중간 생략 ...
  
    spec:
      containers:
      ... 중간 생략 ...
      
        securityContext:
          privileged: true   ## <-- 이 부분
      ... 중간 생략 ...

 

 

게시물 작성자: sejong.jeonjo@gmail.com

 

반응형

 

OCP(Openshift Container Platform)를 사용하다보면, 내용이 방대해서 어디서부터 무엇을 찾아봐야할지 막막할 때가 많다.

그런데 그런 조급한 마음을 접고, 아래 Red Hat Documents를 주제별로 천천히 읽다보면 설정에 관한 대부분 지식이나 운영 지식을 알게 된다.

원래는 영문 문서가 있고, 그 문서를 한글로 번역한 기술 문서인데, 어휘 선택이 잘 된 것 같다. 읽으면서 어색한 것이 별로 없었다.

 

 

한국어(한글) 문서

 

 

Product Documentation for OpenShift Container Platform 4.10 | Red Hat Customer Portal

Access Red Hat’s knowledge, guidance, and support through your subscription.

access.redhat.com

 

영어 문서

 

Product Documentation for OpenShift Container Platform 4.10 | Red Hat Customer Portal

Access Red Hat’s knowledge, guidance, and support through your subscription.

access.redhat.com

 

반응형

 

아래와 같이 `oc edit` 명령으로 insecureRegistries 항목에 Container Image Registry 주소를 추가한다.

 

$  oc edit image.config.openshift.io/cluster

... 중간 생략 ...

spec:
  registrySources:
    insecureRegistries:
    - aaa.cloud.net:8080
    - bbb.cloud.net:5000

... 중간 생략 ...

$

 

그리고 kubernetes node에서 사람이 podman 명령어를 직접 사용하는 경우도 있기 때문에 아래와 같이 /etc/containers/registres.conf 파일에도 registry 주소를 추가한다.

 

##
## File Path:  /etc/containers/registries.conf
##

$  cat  /etc/containers/registries.conf

... 중간 생략 ...

[registries.insecure]
registries = ['aaa.registry.net:8080', 'bbb.registry.net:8080']

... 중간 생략 ...

$
반응형

 

가끔 kubernetes에서 istio를 사용하다보면, Pod 내부의 iptables 구성 상태가 어떠한지 궁금할 때가 있다.

매번 여기저기 명령 찾아서 짜집기하고 있었는데, 이렇게 하니까 찾는 시간도 오래 걸리고, 나의 기억도 오래가지 않아서 여기에 메모를 하고 필요할 때마다 아래 명령을 copy & paste 해야겠다.

 

(이 명령은 Istio README.md에도 있으니까, 좀더 깊게 볼려면 Istio README.md에서 찾아보길~)

 

##
## pod의 container id 확인하기
##

$ ns=ns-a

$ podnm=ratings-v1-587d5cfb9-mxhxc

$ container_id=$(kubectl get pod -n ${ns} ${podnm} -o jsonpath="{.status.containerStatuses[?(@.name=='istio-proxy')].containerID}" | sed -n 's/docker:\/\/\(.*\)/\1/p')

$ echo $container_id
6dbeeb047da9149f0735943cd95885a156dbdc766b48145c40e7346ea7f6bcfb


##
## 어떤 worker node에서 ratings-v1 pod가 구동되어 있는지 확인하기
##

$ kubectl get -n ns-a pod -l app=ratings -o wide
NAME                         READY   STATUS    RESTARTS   AGE     IP               NODE      NOMINATED NODE   READINESS GATES
ratings-v1-587d5cfb9-mxhxc   2/2     Running   0          2d17h   10.244.91.198    node-73   <none>           <none>
ratings-v1-587d5cfb9-s6g6r   2/2     Running   0          2d17h   10.244.142.131   node-75   <none>           <none>


##
## ratings-v1 pod가 구동된 worker node에 ssh 접속하기
##

$ ssh root@node-73

##
## NOTE: worker node에 접속했으면, 위에서 알아냈던 container_id를 아래와 같이 설정하고
##       아래 명령 순서대로 실행한다.
##

[root@node-73 ~]$ container_id=6dbeeb047da9149f0735943cd95885a156dbdc766b48145c40e7346ea7f6bcfb

[root@node-73 ~]$ cpid=$(docker inspect --format '{{ .State.Pid }}' $container_id)


##
## Worker Node의 nsenter 명령을 이용하여 Container 내부의 iptables 정보를 조회한다.
##
[root@node-73 ~]$ nsenter -t $cpid -n iptables -L -t nat -n -v --line-numbers -x

Chain PREROUTING (policy ACCEPT 118222 packets, 7093320 bytes)
num      pkts      bytes target     prot opt in     out     source               destination
1      118224  7093440 ISTIO_INBOUND  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0

Chain INPUT (policy ACCEPT 118224 packets, 7093440 bytes)
num      pkts      bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 10650 packets, 924376 bytes)
num      pkts      bytes target     prot opt in     out     source               destination
1         400    24000 ISTIO_OUTPUT  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0

Chain POSTROUTING (policy ACCEPT 10650 packets, 924376 bytes)
num      pkts      bytes target     prot opt in     out     source               destination

Chain ISTIO_INBOUND (1 references)
num      pkts      bytes target     prot opt in     out     source               destination
1           0        0 RETURN     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:15008
2           0        0 RETURN     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:22
3           0        0 RETURN     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:15090
4      118222  7093320 RETURN     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:15021
5           0        0 RETURN     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:15020
6           2      120 ISTIO_IN_REDIRECT  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0

Chain ISTIO_IN_REDIRECT (3 references)
num      pkts      bytes target     prot opt in     out     source               destination
1           2      120 REDIRECT   tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            redir ports 15006

Chain ISTIO_OUTPUT (1 references)
num      pkts      bytes target     prot opt in     out     source               destination
1           3      180 RETURN     all  --  *      lo      127.0.0.6            0.0.0.0/0
2           0        0 ISTIO_IN_REDIRECT  all  --  *      lo      0.0.0.0/0           !127.0.0.1            owner UID match 1337
3           0        0 RETURN     all  --  *      lo      0.0.0.0/0            0.0.0.0/0            ! owner UID match 1337
4         397    23820 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            owner UID match 1337
5           0        0 ISTIO_IN_REDIRECT  all  --  *      lo      0.0.0.0/0           !127.0.0.1            owner GID match 1337
6           0        0 RETURN     all  --  *      lo      0.0.0.0/0            0.0.0.0/0            ! owner GID match 1337
7           0        0 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            owner GID match 1337
8           0        0 RETURN     all  --  *      *       0.0.0.0/0            127.0.0.1
9           0        0 ISTIO_REDIRECT  all  --  *      *       0.0.0.0/0            0.0.0.0/0

Chain ISTIO_REDIRECT (1 references)
num      pkts      bytes target     prot opt in     out     source               destination
1           0        0 REDIRECT   tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            redir ports 15001

[root@node-73 ~]$

 

 

위 iptables 구성에 관한 이론 및 개념 설명은 아래 blog를 참고하길~

 

 

iptables and netfilter

iptables의 Chain(Rule) & Table 구조 iptables는 다수의 Chain(예: PREROUTING, INPUT, OUTPUT, POSTROUTING, FORWARD)과 그 Chain에 정의된 Rule, 그리고 각 Chain에는 다수의 Table(raw, mangle, nat, filter,..

andrewpage.tistory.com

 

'kubernetes' 카테고리의 다른 글

Kubernetes Metrics Server 설치  (0) 2021.11.26
[TODO] Read - Istio Memo  (0) 2021.11.23
Kubernetes imagePullSecret 설정  (0) 2021.11.19
kubectl 명령 예제 & JSON 포맷으로 출력하기  (0) 2021.11.17
Setup Kubernetes Using Ansible  (0) 2021.11.15
반응형

`kubectl apply` 명령을 통해서 container image를 pull하려면, Docker 회사의 container image pulling에 대한 rate limit 정책 때문에 docker.io 또는 docker.com에서 image를 pulling하지 못하는 경우가 있다.

그래서 일반적으로 docker.com에 회원 가입하고, 무제한으로 image pulling 할 수 있도록 유료 서비스도 이용하는데, `docker login ....` 그리고 `docker image pull ...`하는 것은 잘 되지만, kubectl 명령으로 pod의 container image를 pulling하려면 rate limit제약이 있다. 바로 해당 Pod의 'imagePullSecret' 정보가 없이  때문이다. Pod가 생성될 때, Pod의 manifest에 있는 imagePullSecret 정보를 보고, Container Image Registry에 인증 요청을 하는 것인데 대부분 Pod manifest에 이 imagePullSecret 정보를 설정하지 않는다. (일부러 안 하는 것은 아니고, 이런 것이 있는 줄 모르니까 사용자들이 실수를 한다)

 

가장 흔한 Use Case가 namespace 마다 Docker Registry에 접속할 수 있는 secret을 등록하고, Pod manifest에 이 secret을 참조하도록 하는 것이다.  그런데 이것도 실제 사용하려면 문제가 있다. 내가 직접 작성한 Pod manifest라면, imagePullSecret을 추가하면 끝날 일이지만 istio처럼 istioctl 이라는 명령이 내부적으로 pod manifest를 생성해서 pod 생성을 시도하는 경우라면, imagePullSecret을 내 마음대로 추가하기 어렵다.

 

그래서~  나는 어떻게 이 문제를 풀어야 하지? ㅠㅠ

 

 

우선 즉흥적으로 생각나는 대로 메모를 해보면,  (깊게 생각한다고 풀릴 문제는 아닌 듯...)

  • Cluster 전체에서 공용으로 imagePullSecret을 사용할 수 있도록 설정하는 기능이 있는지 확인한다.
  • ServiceAccount에 Secret을 등록하고, 항상 이 ServiceAccount로 생성되는 Pod에 imagePullSecret을 사용할 수 있도록 한다.
  • istioctl 같은 별개의 deployment 관리 명령이 있는 경우라면, istioctl option 중에서 imagePullSecret을 설정하는 parameter가 있는지 찾아본다.

Web Docs를 잘 찾아보니까, 고맙게도 kubernetes.io의 Web Docs에 내가 고민하고 있는 내용을 잘 설명해주고 있다.

https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry

 

Images

A container image represents binary data that encapsulates an application and all its software dependencies. Container images are executable software bundles that can run standalone and that make very well defined assumptions about their runtime environmen

kubernetes.io

 

 

위 Web Docs에서는 아래의 4 가지 답안을 줬는데, 가장 사람 손을 덜 타는 (A) 방법으로 문제를 풀어봤다.

 

(A) Configuring Nodes to Authenticate to a Private Registry      ## 내가 선택한 해결 방법
    - all pods can read any configured private registries
    - requires node configuration by cluster administrator

(B) Pre-pulled Images
    - all pods can use any images cached on a node
    - requires root access to all nodes to setup

(C) Specifying ImagePullSecrets on a Pod
    - only pods which provide own keys can access the private registry

(D) Vendor-specific or local extensions
    - if you're using a custom node configuration,
       you (or your cloud provider) can implement your mechanism for authenticating the node to the container registry.

 

 

그래서 위 (A) 방법대로 문제를 해결했다~  자세한 절차는 아래 Web Docs에 있다.  단순하게 따라하면 잘 동작한다.


https://kubernetes.io/docs/concepts/containers/images/#configuring-nodes-to-authenticate-to-a-private-registry

위 원문을 조금 요약해보자면 아래와 같다.

##
## If you run Docker on your nodes, 
## you can configure the Docker container runtime to authenticate to a private container registry.
## This approach is suitable if you can control node configuration.

## Here are the recommended steps to configuring your nodes to use a private registry. 
## In this example, run these on your desktop/laptop PC:

##   1. Run docker login "docker.io" for each set of credentials you want to use. 
##      This updates $HOME/.docker/config.json on your PC.
##   2. View $HOME/.docker/config.json in an editor to ensure it contains only the credentials you want to use.
##   3. Get a list of your nodes; for example:
##     - if you want the names: 
##       nodes=$( kubectl get nodes -o jsonpath='{range.items[*].metadata}{.name} {end}' )
##     - if you want to get the IP addresses: 
##       nodes=$( kubectl get nodes -o jsonpath='{range .items[*].status.addresses[?(@.type=="ExternalIP")]}{.address} {end}' )
##   4. Copy your local .docker/config.json to one of the search paths list above.
##     - for example, to test this out: 
##       for n in $nodes; do scp ~/.docker/config.json root@"$n":/var/lib/kubelet/config.json; done
##

위 설명대로 /root/.docker/config.json 파일을 모든 master node, worker node의 /var/lib/kubelet/config.json 경로에 복사한 후에 아래와 같이 Pod를 deploy하면서  docker.io에서 rate limit이 걸리지 않고 container image를 pulling 할 수 있는지 확인한다. 

 

$  kubectl  apply -f -  <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: busybox-sleep
spec:
  containers:
  - name: busybox
    image: busybox
    imagePullPolicy: Always
    args:
    - sleep
    - "1000000"
EOF

pod/busybox-sleep created

$ kubectl get pod
NAME            READY   STATUS    RESTARTS   AGE
busybox-sleep   1/1     Running   0          7s
$

 

주의:
나는 docker.io 유료 가입자이다.  이 글을 읽는 분도 본인이 사용하는 kubernetes cluster가 container image pulling rate limit 없이 동작하게 하려면 꼭 docker.io에 유료 가입해야 한다)

 

 

참고:

가장 일반적인 Use Case는 아래와 같이 namespace에 secret을 생성하고, 이 regcred 라는 secret을 Pod manifest에서 참조하도록 한다. 그런데, 모든 Pod의 manifest를 수정해야 하는 노동력이 추가되므로 이 방법을 권장하지 않는다.

(아마 imagePullSecret 정보를 자동으로 patch해주는 Mutating Admission Controller / WebHook Server를 만들어서 나의 Kubernetes Cluster에 추가해주면 사람이 개입할 일은 없겠지만, Webhook Server를 작성하는 것도 일인지라... ㅠㅠ)

 

$  kubectl create  -n ${MY-NS}  secret docker-registry  regcred  --docker-server=docker.io  --docker-username=${MYID}  --docker-password=${MYPW}

 

 

참고:

이 container image pulling rate limit 해결 방법은 순정 kubernetes cluster에서만 유효하고, Red Hat Openshift(OCP)는 Docker Registry 인증 문제를 해결하는 방식이 다르다. (결론만 말하자면, Red Hat OCP 쪽이 더 명령이 간단하고 쉽게 해결된다)

Red Hat에 비용을 내고 기술 지원 서비스를 받으면, Red Hat 직원이 알아서 해결해줬기 때문에 굳이 남의 밥벌이 영역의 일을 이렇게 공개된 글에 올리는 것은 예의가 아닌 것 같아서, Red Hat OCP에서의 Docker Registry 인증 문제에 대한 해결 방법은 언급하진 않겠다.

 

+ Recent posts