Kubernetes

23.9.7(목) 쿠버네티스 9일차

사실 나도 모름 2023. 9. 7. 13:07

 

https://kubernetes.io/ko/docs/concepts/scheduling-eviction/assign-pod-node/

 

노드에 파드 할당하기

특정한 노드(들) 집합에서만 동작하거나 특정한 노드 집합에서 동작하는 것을 선호하도록 파드를 제한할 수 있다. 이를 수행하는 방법에는 여러 가지가 있으며 권장되는 접근 방식은 모두 레이

kubernetes.io

1. Affinity

Affinity 종류

1. NodeAffinity

어느 노드에 파드를 만들 것인지 선호도를 지정

 

1-1. anti-affinity

affinity의 반대, 선호하지 마라

 

2. PodAffinity

어느 파드에 컨테이너를 만들 것인지 선호도를 지정


선호도

1. required

선호되는 노드를 지정할 때 강제로 지정

 

2. preferred

선호되는 노드를 지정할 때 반강제로 지정(만들수도 있고 안만들수도 있다.)

가중치(weight)를 이용하여 비율적으로 분배되도록 지정 가능

 


연산자

1. In

value중에 하나라도 일치하면

 

2. NotIn

value중에 일치하는 게 없으면

 

3. Exists

존재한다면

key의 존재유무로 판단하기 때문에 value를 보지않는다.

 

4. doesNotExist

존재하지 않으면

 

5. GT

value보다 크면

 

6. LT

value보다 작으면


1) Node Affinity

1. Required

kubectl label nodes node1.k8s.local requiredkey=worknodereqkey

kubectl label nodes node2.k8s.local preferredkey2=worknodeprekey2 

kubectl label nodes node3.k8s.local preferredkey3=worknodeprekey3

key 레이블을 각 노드에 입력 

 

cat << EOF > pod-required-affinity-1.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx-1
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: requiredkey
            operator: In
            values:
            - worknodereqkey
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
EOF

연산자를 In으로 지정하여 실행하면 requiredkey라는 이름을 가진 key를 찾고 value값이 일치하는 노드에 대해 파드가 생성된다.

 

cat << EOF > pod-required-affinity-2.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx-2
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: requiredkey
            operator: Exists
#            values:
#            - worknodereqkey
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
EOF

연산자를 Exists으로 지정하여 실행하면 requiredkey라는 이름을 가진 key가 존재하면 해당 노드에 파드가 생성된다.

여기서 주의할 것은 Exists는 key가 있는지 검토만하고 실행하기 때문에 value를 보지않고 key의 존재유무만으로 파드를 생성한다.

 

 


2. Preferred

cat <<EOF > pod-preferred-affinity-1.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx-3
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 20
        preference:
          matchExpressions:
          - key:  preferredkey2
            operator: In
            values:
            - worknodeprekey2
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 30
        preference:
          matchExpressions:
          - key:  preferredkey3
            operator: In
            values:
            - worknodeprekey3
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
EOF

가중치를 node2에는 20, node3에는 30을 주어 파드가 생성되는 선호도를 지정

 

cat<<EOF>pod-req-prefer-1.yaml
apiVersion: v1
kind: Pod
metadata:
  name: with-affinity-1
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: requiredkey
            operator: In
            values:
            - worknodereqkey
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: preferredkey2
            operator: In
            values:
            - worknodeprekey2
      - weight: 50
        preference:
          matchExpressions:
          - key: preferredkey3
            operator: In
            values:
            - worknodeprekey3
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
EOF

preferred보다 required가 우선된다.

 


2) Pod Affinity

cat <<EOF > pod-nginx-with-label-security.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    security: S1
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
EOF

파드에 "security: S1" 레이블을 지정

nginx-pod 가 node3에 생성되었다.

 

cat <<EOF > pod-nginx-with-label-security-node.yaml


apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-affinity
    image: nginx
EOF

pod affinity로 키는  security, value는 S1인 파드가 있는 node3에 생성

topologyKey: kubernetes.io/hostname는 파드 어피니티 규칙을 적용할 때 호스트네임을 사용하여 파드를 스케줄링하는 것을 지정한다.

 

cat <<EOF > pod-nginx-with-label-anti-affinity.yaml


apiVersion: v1
kind: Pod
metadata:
  name: with-pod-antiaffinity
spec:
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S1
          topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-antiaffinity
    image: nginx
EOF

Security: S1 레이블을 가진 파드가 있는 node3이 아닌 곳에 파드를 생성해라는 뜻이다.

가중치(weight)는 100, 다시말해 node3에는 절대 생성하지 말라는 의미

아래 화면에 with-pod-antiaffinity가 node1에 생성됨을 볼 수 있다.

이것이 AntiAffinity

 

 


https://kubernetes.io/ko/docs/concepts/services-networking/network-policies/

 

네트워크 정책

IP 주소 또는 포트 수준(OSI 계층 3 또는 4)에서 트래픽 흐름을 제어하려는 경우, 네트워크 정책은 클러스터 내의 트래픽 흐름뿐만 아니라 파드와 외부 간의 규칙을 정의할 수 있도록 해준다. 클러

kubernetes.io

2. 네크워크 정책

 

 

네트워크정책을 쉽게 방화벽이라 생각하면 된다.

stateless 개념을 가지고 있다.

 

예시

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
    - Ingress
    - Egress
  ingress:			# 들어오는 것
    - from:
        - ipBlock:
            cidr: 172.17.0.0/16
            except:
              - 172.17.1.0/24
        - namespaceSelector:
            matchLabels:
              project: myproject
        - podSelector:
            matchLabels:
              role: frontend
      ports:
        - protocol: TCP
          port: 6379
  egress:			# 나가는 것
    - to:
        - ipBlock:
            cidr: 10.0.0.0/24
      ports:
        - protocol: TCP
          port: 5978

CNI에 따라서 Network Polices가 적용이 될 수도 있고 안될 수도 있다.

만약 내가 운영하고 있는 쿠버네티스에서 인그레스나 이그레스가 적용이 안된다면 위 사진을 참조

혹은 아래 방법 참조


1. 기존의 Flannel 삭제
기존의 Flannel을 유지한 상태로 Canal을 설치 시도 했지만 다양한 오류가 발생해서
일단은 Flannel을 삭제하고 진행함. 예상으로는 기존에 사용하던 Flannel version이 낮아서 문제가 발생한 것 같기도함.

[명령어]
kubectl delete -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

 2. 모든 kubernetes Node docker restart
master node 를 포함한 모든 worker node의 docker를  restart 해줘야함

[명령어]
service docker restart

3. DataStore 종류 선택.
Calico 설치 방법은 Datastore에 종류에 따라서 etcd, kubernetes API Datastore 2가지로 나누어 짐.
Canal 설치 시에서도 DataStore를 선택해줘야함.

etcd 
for direct connection to an etcd cluster

[장점]
1. 추가 DataStore가 필요하지 않으므로 관리가 더 간단함.
2. Kubernetes RBAC를 사용하여 Calico 리소스에 대한 액세스를 제어 할 수 있음.
3. Kubernetes audit logging을 사용하여 Calico 리소스 변경에 대한 audit logging를 생성 할 수 있음.
kubernetes API Datastore
for connection to a Kubernetes API server

 

[장점]
1. 비 Kubernetes 플랫폼 (예 : OpenStack)에서 Calico를 실행할 수 있음.
2. Kubernetes와 Calico 리소스 간의 문제를 분리 할 수 있음
3. Calico Cluster를 실행할 수 있음.

예제 환경에서는 kubernetes API Datastore으로 설치해보겠음.


4. Kube-Controller 모듈 Flag 설정
 설정을 해야하지만 kubernetes 로컬 설치 과정에서 이미 했기 때문에 생략함.
참고 : 03. Kubernetes 로컬설치, 환경 예제 (Master Node 클러스터 구성 부분)

[명령어]
kubeadm init --pod-network-cidr=<your-pod-cidr>
 

5. Canal 설치 용 yaml파일을 Download
[명령어]
curl https://docs.projectcalico.org/manifests/canal.yaml -O
 

6. Canal 설치
[명령어]
kubectl apply -f canal.yaml

7. Flannel 설치
[명령어]
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

 8. 모든 kubernetes Node docker restart
 master node 를 포함한 모든 worker node의 docker를  restart 해줘야함

[명령어]
service docker restart

9. Canal 설치 확인
결과를 확인해보면 기존에 설치되어있던 Flannel 이 그대로 있으면서 Calico 가 추가된 것을 확인 할 수 있음.

[명령어]
kubectl get all -n kube-system


kubectl run app-pod --image=nginx
kubectl run web-pod --image=nginx

컨테이너 생성

kubectl expose pod app-pod --port=80
kubectl expose pod web-pod --port=80

80번 포트 개방

kubectl exec -it app-pod -- /bin/bash

접속이 되는지 확인

kubectl exec -it web-pod -- curl app-pod

외부에서 접근이 되는지 확인

 

cat <<EOF > default-deny.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
EOF

기본적으로 들어오는 것, 나가는 것 모두 차단

 

 

kubectl exec -it web-pod -- curl app-pod

네트워크 정책으로 인해 접근 못함

 

kubectl exec -it app-pod -- curl web-pod

네트워크 정책으로 인해 접근 못함

 

cat <<EOF > allow-egress.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-egress
  namespace: default
spec:
  podSelector:
    matchLabels:
      run: app-pod
  policyTypes:
    - Egress
  egress:
  - to:
    - podSelector:
        matchLabels:
          run: web-pod
EOF

위 정책을 적용하면 app에서 web으로 내보내는 것이 허용된다.

하지만 내보내는 것을 허용해봤자 들어오는 것부터 차단되어 있으므로 접근 불가

 

cat <<EOF > allow-ingress.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress
  namespace: default
spec:
  podSelector:
    matchLabels:
      run: web-pod
  policyTypes:
    - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          run: app-pod
EOF

위 정책을 적용하면 web에서 app으로 들어오는 것이 허용된다.

그래도 안된다.

이는 IP정보를 직접 명시해주면 해결이 가능하다.

일반적으로 pod과 pod 사이에서 통신할 때는 이름으로 통신이 가능하지만 현재 요청을 보내는 위치가 node이므로 이름으로 통신이 불가능하기때문에 IP를 명시해줘야 한다.

kubectl exec -it app-pod -- curl [web-pod의 IP]

정상적으로 응답을 받았다.

 

적용된 네트워크 정책이다.

 

 

허용해준 파드에 대해서 web은 app으로부터 요청을 받고 응답을 내보내는 것만 허용했기 때문에 당연히 역은 성립하지 않는다.

안된다.


기존의 default-deny.yaml을 제외하고 모두 삭제한 뒤 실습

cat <<EOF > allow-egress-1.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-egress-1
  namespace: default
spec:
  podSelector:
    matchLabels:
      run: app-pod
  policyTypes:
    - Egress
  egress:
  - to:
    - podSelector:
        matchLabels:
          run: web-pod
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-egress-2
  namespace: default
spec:
  podSelector:
    matchLabels:
      run: web-pod
  policyTypes:
    - Egress
  egress:
  - to:
    - podSelector:
        matchLabels:
          run: app-pod
EOF

---

cat <<EOF > allow-ingress-1.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-1
  namespace: default
spec:
  podSelector:
    matchLabels:
      run: web-pod
  policyTypes:
    - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          run: app-pod
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-2
  namespace: default
spec:
  podSelector:
    matchLabels:
      run: app-pod
  policyTypes:
    - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          run: web-pod
EOF

---

cat<<EOF>network-policy-pods.yaml
apiVersion: v1
kind: Pod
metadata:
  name: http-go-v1
  labels:
    app: http-go-v1
spec:
  containers:
  - name: http-go
    image: nginx
    ports:
    - containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
  name: http-go-v2
  labels:
    app: http-go-v2
spec:
  containers:
  - name: http-go
    image: nginx
    ports:
    - containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
  name: http-go-v3
  labels:
    app: http-go-v3
spec:
  containers:
  - name: http-go
    image: nginx
    ports:
    - containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: http-go-v1
spec:
  selector:
    app: http-go-v1
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: http-go-v2
spec:
  selector:
    app: http-go-v2
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: http-go-v3
spec:
  selector:
    app: http-go-v3
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
EOF

---

cat<<EOF>network-policy-pods-np.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: http-go-v1-ingress
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: http-go-v1
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: http-go-v2 # http-go-v1은 http-go-v2로 부터 들어오는것 허용

---

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: http-go-v2-ingress
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: http-go-v2
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: http-go-v3 # http-go-v2는 http-go-v3으로 부터 들어오는것 허용

---

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: http-go-v3-ingress
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: http-go-v3 # http-go-v3은 어느곳으로 부터 들어오는것 허용안함
  policyTypes:
  - Ingress
EOF

 

 

 


3. Helm

centos의 yum, ubuntu의 apt와 같은 것이라 보면 된다.

 

1. 첫번째 방법

필자는 두번째 방법을 성공했으므로 실패하는 것을 보기 싫으면 두번째로 이동

wget https://get.helm.sh/helm-v3.3.0-linux-amd64.tar.gz

tar xvzf helm-v3.3.0-linux-amd64.tar.gz

cd linux-amd64/

mkdir /usr/local/bin/helm/

mv * /usr/local/bin/helm/

# 환경변수 설정 시 .bashrc파일의 제일 밑에 아래 PATH를 붙여넣고 source .bashrc해도 됨
PATH=$PATH:$HOME/bin:/usr/local/bin/helm

helm 명령을 쓰기 위한 절차

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add brigade https://brigadecore.github.io/charts
helm repo add stable https://charts.helm.sh/stable

레포지토리 추가

helm repo update

업데이트

helm search repo stable

helm search repo bitnami

레포지토리에서 사용가능한 내용들

helm repo add incubator https://charts.helm.sh/incubator
helm repo add mysql-operator https://mysql.github.io/mysql-operator/

새로운 레포지토리 추가

 

위에서 했던 모든 파드와 네트워크 폴리시를 지우고

mkdir helmcharts

cd helmcharts/

helm pull bitnami/nginx

tar xzvf nginx-15.2.0.tgz

 


※ more 와 less 의 차이

more : 읽기 전용

  • nano, vi와 비슷
  • v를 누르면 편집모드 진입
  • /는 찾기

less : 읽기, 쓰기 가능


more values.yaml
# 파일 내부에 replicaCount: 1 을 3으로 고치고
helm install nginx -f values.yaml

하지만 에러가 뜬다.

아무래도 소스가 잘못된 것 같다.

원래대로 되돌리자.

 

rm -rf /usr/local/bin/helm

vi .bashrc
# 제일 밑에 PATH지정해놓은 것을 삭제 후 source .bashrc

echo $PATH
# PATH가 없어진 것을 확인
# 만약 여전히 존재하면 exit로 root에서 나갔다가 다시 sudo -i
# 나갔다가 다시 접속하면 임시로 저장되어있던 PATH들은 모두 사라짐

두번째 방법

curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3

chmod 700 get_helm.sh

./get_helm.sh

apt install git make
# 아마 에러가 뜰 것이다.

# snap install go
# 보안상 문제로 --classic을 사용하도록 유도함.

snap install go --classic
# --classic 옵션을 주어 설치하면 된다.

git clone https://github.com/helm/helm.git

cd helm

make

snap install go --classic 을 사용하여 go를 설치할 것

위와 같이 설치 후

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add brigade https://brigadecore.github.io/charts
helm repo add stable https://charts.helm.sh/stable
helm repo add incubator https://charts.helm.sh/incubator
helm repo add mysql-operator https://mysql.github.io/mysql-operator/

레포지토리를 다시 추가

helm pull bitnami/nginx

tar xvfz  nginx-15.2.0.tgz

bitnami/nginx 가져온 후

cd nginx

helm install nginx -f my-values.yaml .

드디어 성공
파드도 잘 생성되었음
로드밸런서의 외부 IP를 확인하고
접속하면 웹서버가 표시됨

 

nginx 제거

helm get manifest nginx	# yaml를 볼 수 있음

helm uninstall nginx 	# 삭제

nginx의 manifest 파일
삭제하는 법

 

'Kubernetes' 카테고리의 다른 글

Kubernetes 기초 - Pod(1)  (0) 2023.12.06
Kubernetes 기초 - 동작 원리  (0) 2023.12.06
23.9.6(수) 쿠버네티스 8일차  (0) 2023.09.06
23.9.4(월) 쿠버네티스 7일차  (0) 2023.09.04
23.9.1(금) 쿠버네티스 6일차  (0) 2023.09.01