DevOps

[Kubernetes] 쿠버네티스 환경 구성하기 - rook ceph, OCI registry, chartmuseum, ingress controller

네른 2022. 5. 23. 21:08

앞선 글대로 Kubernetes를 설치하면, 말 그대로 kube cluster만 구성된 상태가 된다.

여기에 먼저 설정한 것들이 위에 적힌 것들이다.

 - rook ceph : kubernetes 환경 위에 가상 스토리지인 ceph를 배포하여 사용하게 해주는 도구.

 - chartmuseum : 프라이빗 차트 레포. helm chart를 사용할 때, 프라이빗 레포가 필요하다면 설치.

 - ingress controller : ingress rule을 적용하기 위해 필요한 controller

 - OCI registry : docker 환경을 이용한 프라이빗 레지스트리.

 

사용하는 환경이 프라이빗 레포에 레지스트리가 필요하고

helm chart로 관리를 하고자 하였기때문에 chartmuseum, OCI registry를 적용하였다.

rook ceph의 경우, 로컬 저장소를 사용하고자 적용하였으며 ingress controller는 항상 하던거라 하는김에 정리

 

 


rook ceph

 - Ceph라는 가상 스토리지를 kubernetes 환경에 띄울 수 있도록 도와주는 'rook'.

 - 스토리지 오케스트레이터로, 클러스터 내에 분산된 스토리지를 구성하고 관리해준다.

 - 설치하면서 겪은바로는, 가급적이면 깨끗한(아무것도 설치되지 않은) 디스크 세개 이상을 이용해 구성하는 것을 추천.

 

rook으로 ceph cluster를 구성하는 방법은 어렵지 않다. rook에서 잘 작성된 yaml 파일들을 이미 제공해주므로, 필요한 옵션만 살짝 바꾸면서 구성하면 된다.

 

git clone https://github.com/rook/rook.git  # 특정 릴리즈 브랜치만 가져와도 됨
cd rook/deploy/examples/ # 다양한 예제가 들어있다.


kubectl create -f crds.yaml
kubectl create -f common.yaml
kubectl create -f operator.yaml
kubectl create -f cluster.yaml

# 위 세 yaml을 설치하면 기본적으로 3개의 디스크를 이용한 rook ceph 클러스터가 구성됨
# 설치되는 namespace는 rook-ceph
# 필요한 설정은 yaml을 수정하면 됨. 주로 common.yaml을 수정하였음.

kubectl  apply  -f toolbox.yaml
# 이후 위 명령어로 toolbox pod를 생성
# 여기에는 rook 테스트/디버깅을 위한 도구들이 들어있다.

kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- bash
# 위 명령어를 통해 해당 pod로 들어가서 ceph 명령어를 사용할 수 있다.
ceph status
# ceph cluster의 현 상태를 볼 수 있다.

이 때 yaml파일에 주의할점이 하나 있는데, common.yaml에 mgr/mon 의 count를 잘 조절해야 한다.

기본적으로 allowMultiplePerNode 라는 옵션이 false로 되어있어 한 노드에 여러개의 mgr와 mon을 띄울 수 없다.

그러므로 현재 내 클러스터의 노드 수와 위 mgr/mon 수를 잘 맞추던지 false를 true로 바꾸면 된다.

 

만약 테스팅을 위해 단일 노드로 구성된 클러스터를 만들고 싶으면 cluster.yaml 대신 cluster-test.yaml을 사용하면 된다.

찾아보니 rook에서는 최소 3개 이상의 노드로 구성된 클러스터를 기반으로 셋팅이 되어있는 것으로 보인다.

 

위 과정을 진행하면서, 마스터 노드에 POD가 pending 상태로 멈추어있는 것을 보았는데, 이는 다음과 같은 이유였다.

 - 기본적으로 kubernetes는 master node에는 pod를 띄우지 않는 것을 원칙으로 함.

     : 예전에 처음할때는 이런거 없었던 것 같은데..

 - 0/2 nodes are available: 1 node(s) didn't match pod anti-affinity rules, 1 node(s) had taint {node-role.kubernetes.io/master: } 그래서 이러한 오류가 발생한다.

 - 찾아보니 이는 taint와 toleration 이라는 옵션과 연관이 있다고 하는데. 이는 다음 글에서 설명하기로 한다.

 - 아무튼 해결방법은 두가지인데, 

kubectl taint node 노드이름 node-role.kubernetes.io/master:NoSchedule-

요 명령어를 수행하던지, yaml 파일에

     tolerations:
      - key: node-role.kubernetes.io/master
        operator: Equal
        effect: NoSchedule

요 내용을 추가하면 된다.

 

이러면 간단한 ceph 클러스터가 구성된다.

사실 이부분은 또 따로 배워야할게 너무 많은거같아서 사용만 해봤지 깊게는 모른다. 좀 더 찾아봐야 될 것 같다.

 


chartmuseum

 - helm chart repository

 - 차트의 경우, 어디선가 tar 파일을 받아올 수 있는 환경이 필요하며, 이를 위한것이 chartmuseum

 - 기본적으로 docker image를 잘 제공해서 docker에 띄워두고 쓰기에 편하다.

 - 그렇지만, docker에 띄워두면 누군가는 또 그 docker를 관리해야 하기에, 우리의 강력한 오케스트레이션 도구에 올려보자.

 

기본적으로 ingress / service / deploy로 구성할 것이며, 여기서는 replica가 1인 채 구성할 것.

apiVersion: v1
kind: Service
metadata:
  namespace: testnm
  name: test-helm
  labels:
    app: helm-repository
spec:
  selector:
    app: helm-repository
  ports:
  - protocol: TCP
    port: 443
    targetPort: 8080

 

kind: Ingress
apiVersion: extensions/v1beta1
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx-shd
    nginx.ingress.kubernetes.io/backend-protocol: HTTPS
    nginx.ingress.kubernetes.io/proxy-connect-timeout: '3600'
    nginx.ingress.kubernetes.io/proxy-read-timeout: '3600'
    nginx.ingress.kubernetes.io/ssl-redirect: 'true'
  name: helm-repository
  namespace: testnm
  labels:
    app: helm-repository
spec:
  tls:
    - hosts:
        - 호스트명
      secretName: helm-repository-tls
  rules:
    - host: 호스트명
      http:
        paths:
          - path: /
            backend:
              serviceName: helm-repository
              servicePort: 443

이렇게 각각 service와 ingress를 정의해주고


 

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: testnm
  name: helm-repository
  labels:
    app: helm-repository
spec:
  selector:
    matchLabels:
      app: helm-repository
  replicas: 1
  template:
    metadata:
      labels:
        app: helm-repository
    spec:
      containers:
        - name: chartmuseum
          image: "chartmuseum/chartmuseum:v0.12.0"
          imagePullPolicy: Always
          ports:
            - containerPort: 8080
              name: web
          env:
            - name: DEBUG
              value: "1"
            - name: STORAGE
              value: "local"
            - name: STORAGE_LOCAL_ROOTDIR
              value: "/charts"
            - name: BASIC_AUTH_USER
              value: "유우우저"
            - name: BASIC_AUTH_PASS
              value: "비밀번호"
            - name: AUTH_ANONYMOUS_GET
              value: "1"
            - name: TLS_CERT
              value: "/certs/tls.crt"
            - name: TLS_KEY
              value: "/certs/tls.key"
          resources:
            requests:
              memory: "64Mi"
              cpu: "250m"
            limits:
              memory: "2Gi"
              cpu: "2"
          volumeMounts:
            - name: charts
              mountPath: /charts
            - name: certs
              mountPath: /certs
      volumes:
        - name: certs
          secret:
            secretName: helm-repository-tls
            defaultMode: 420
        - name: charts
          persistentVolumeClaim:
            claimName: helm-repository

deployment를 딱 정의해주면 구성이 되기에 앞서 오류가 뜬다.

아직 ingress controller도 없고, pv/pvc가 사용할 storageclass도 없기 때문!


이에 앞서, 사용할 secret부터 정의해주자.

apiVersion: v1
kind: Secret
metadata:
  name: helm-repository-tls
  namespace: testnm
  labels:
    secret: tls
type: kubernetes.io/tls
data:
  tls.crt: 비밀이지=
  tls.key: 비밀이야

tls 설정을 위한 cert와 key가 필요한데, 우선은 self-signed 된 것을 사용하자.

openssl req -new -x509 -days 3600 -subj "/C=KR/ST=Seoul/O=tmax/CN=설정한hostname" \
                  -addext "subjectAltName = DNS:설정한hostname" \
                  -newkey rsa:2048 -nodes -keyout tls.key -out tls.crt
                  
                  
cat tls.crt | base64 | tr -d '\n'
cat tls.key | base64 | tr -d '\n'  # secret은 base64로 인코딩 해서 저장해야 잘 동작한다

위에 보면 subj, AltName같은걸 추가해주는데, 이를 SAN(Subject Alternative Name)이라고 한다. 

kubernetes가 인증서를 사용할 때 SAN 인증서를 사용하도록 설정이 되어있기 때문에, 이에 맞추어 인증서를 SAN 으로 구성한다.

 - SAN이란 여러 호스트네임을 하나의 인증서에 담는 것 이라고 생각하면 된다.


 

아무튼, 그다음으로는 storageclass를 만들건데 이것도 rook에서 제공하는 예제를 사용할 것.

kubectl create -f csi/cephfs/storageclass.yaml

이러면 rook-ceph-block 이라는 이름의 storageclass가 생성되고, 우리는 이걸 사용할 것.

 

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: helm-repository
  namespace: testnm
  labels:
    app: helm-repository
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: rook-ceph-block

용량은 필요하면 더 늘려써도되고, accessMode도 필요하면 더 늘려도 되지만, 우선은 한개의 pod에서만 접근할거고 테스트용이기에 readwriteonce에 1Gi로 설정.

 

pv는 dynamic provisioning에 의해 알아서 생성될 것. 이는 rook에 의해 생성된? 생성한 storageclass에 의해 수행된다.

 

 

여기까지 왔으면 이제 남은건 이 서비스를 외부에 노출시키는 것이다.

 - 그런데 이미 ingress와 service를 만들었는데 왜 접근이 되지 않는가???

 

이는 ingress controller가 아직 없기 때문.

 - cloud 환경을 사용하면 보편적으로는 ingress controller가 잘 구성되어 있음.

 - 그러나 지금은 베어메탈 위에 직접 설치했으므로 ingress controller를 하나 달아주어야 함.

 

https://kubernetes.github.io/ingress-nginx/deploy/#quick-start

 

Installation Guide - NGINX Ingress Controller

Installation Guide There are multiple ways to install the NGINX ingress controller: with Helm, using the project repository chart; with kubectl apply, using YAML manifests; with specific addons (e.g. for minikube or MicroK8s). On most Kubernetes clusters,

kubernetes.github.io

요걸 보고 잘 따라가면 된다. nginx를 이용한 ingress controller를 구성하는 과정임!

 - 추가적으로 찾아보다 안건, ingress controller는 namespace와 상관없이 클러스터 내에 단 하나만 있으면 된다.

 - 단지 ingress rule만 그 해당되는 서비스와 동일한 namespace에 있으면 되는 것.

 - 또한, ingress rule 설정할 때, ingress의 annotation에 kubernetes.io/ingress.class: "nginx" 이거 꼭 넣어주자. kubernetes가 버전업되면서 ingress에 ingress class를 지정해주어야 한다고 함!

 

여기까지 진행하면, 앞서 생성한 chartmuseum이 정상적으로 클러스터 위에 뜨게 된다.

 - chartmuseum도 설치했고

 - rook ceph 로 클러스터 구성도 했고

 - ingress controller도 설치된 것.

 

이제 helm repo add --ca-file tls.crt test 명령어로 helm chart repo를 추가하면 된다.

 


그런데, helm을 잘 보면 pull은 있는데 push가 없다. 원래는 curl 등으로 tar 파일을 올려두라는 의미인데, 불편하므로 push를 사용할 수 있도록 플러그인을 설치하자.

 

cm-push

https://github.com/chartmuseum/helm-push

 

GitHub - chartmuseum/helm-push: Helm plugin to push chart package to ChartMuseum

Helm plugin to push chart package to ChartMuseum. Contribute to chartmuseum/helm-push development by creating an account on GitHub.

github.com

helm의 cm-push 플러그인을 설치하고, 사용하면 된다.

helm cm-push package이름 repo이름 --insecure -u 유저 -p 패스워드

이렇게 사용하면 되는데, insecure는 위와 같이 사용하는 경우 self-signed 된 인증서로 인해 x509 에러가 발생하므로 이를 무시하기 위한 것. 제대로 서비스할때는 안쓰는게좋다.

 

그런데, 이와 같이 실행하다보니 push가 잘 안되더라. 무슨 permission 에러가 발생하는데, 읽어보니 차트를 올릴 디렉토리에 대한 권한 문제로 보인다.

 

그래서 해당 컨테이너를 들여다보니 chart 폴더가 root:root로 되어있고, 해당 프로세스는 1000 유저로 실행되고 있던 것.

 - 앞서 살펴보았던 securityContext로 runAsUser: 0  / runAsGroup: 0 을 설정하고 실행했다.

 - 이것보단 폴더의 권한을 낮추는 방법이 좋긴한데, 조금 복잡해져서 우선 테스트때는 이렇게 진행하는 것으로. 실제 사용할때에는 다른방법을 찾아보아야겠다.

 

 

여기까지하면 기본적인 구성은 완료된다.

이 외에 OCI registry가 있는데, 

 - OCI registry? Open Container Initiative 라는 컨테이너 개방형 표준을 설계하는 기관에서 제공하는 컨테이너 레지스트리 서비스.

 - docker 환경에 구성이 가능하며, https://helm.sh/ko/docs/topics/registries/  위 링크대로 사용하면 된다.

 - 위의 방식대로 사용하면  helm과 연결지어서 사용할 수 있다.

 - 처음에는 이 방식대로 구성하려다가, docker 위에 동작하기보다는 kubernetes 환경에 구축하고싶어 chartmuseum을 올리고 사용하게 되었다.

 - 생각보다 이 helm oci에 대한 자료가 많이 없었던 것도 한몫 했다.