Ubuntu

[Ubuntu] 쿠버네티스 (4일차)

bo._.h 2022. 10. 2.
728x90
반응형

디플로이먼트

https://kubernetes.io/ko/docs/concepts/workloads/controllers/deployment/

디플로이먼트 작성하기

cat <<EOF > nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3 # 복제본 수 
  selector:
    matchLabels:
      app: nginx # 셀렉터
  # 파드의 모양
  template:
    metadata:
      labels:
        app: nginx # 파드의 레이블
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
EOF

kubectl apply -f nginx-deployment.yaml

디플로이먼트와 함께 레플리카셋과 파드가 잘 생성되는지 확인한다.

$ kubectl get deploy,rs,pod
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   3/3     3            3           12s

NAME                               DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-7fb96c846b   3         3         3       12s

NAME                         READY   STATUS    RESTARTS   AGE
pod/nginx-7fb96c846b-4whk8   1/1     Running   0          12s
pod/nginx-7fb96c846b-hh7xv   1/1     Running   0          12s
pod/nginx-7fb96c846b-wflp9   1/1     Running   0          12s

레플리카셋의 파드 복원 기능 테스트

파드를 하나 강제로 삭제하고 레플리카셋이 이것을 복구하는 지 확인해보자.

kubectl delete pod nginx-7fb96c846b-4whk8 

$ kubectl get pod          
NAME                     READY   STATUS    RESTARTS   AGE
nginx-7fb96c846b-hh7xv   1/1     Running   0          101s
nginx-7fb96c846b-p5tnl   1/1     Running   0          7s
nginx-7fb96c846b-wflp9   1/1     Running   0          101s

파드의 스케일 수정하기

kubectl scale deployment nginx --replicas=10 # 확장
kubectl scale deployment nginx --replicas=5 # 축소

연습문제

  1. jenkins/jenkins 이미지를 사용해 디플로이먼트 deploy-jenkins를 생성하라.
  2. jenkins 디플로이먼트로 배포되는 앱을 app: jenkins-test로 레이블링하라.디플로이먼트가 잘 생성되었는지 확인한다.
  3. $ kubectl get pod -w NAME READY STATUS RESTARTS AGE deploy-jenkins-8688796664-75c4n 1/1 Running 0 25s deploy-jenkins-8688796664-klpnr 1/1 Running 0 25s deploy-jenkins-8688796664-ldx7r 1/1 Running 0 25s
  4. cat <<EOF > jenkins-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: deploy-jenkins spec: replicas: 3 selector: matchLabels: app: jenkins-test # 셀렉터 # 파드 템플릿 template: metadata: labels: app: jenkins-test # 레이블 spec: containers: - name: jenkins image: jenkins/jenkins ports: - containerPort: 8080 EOF kubectl apply -f jenkins-deployment.yaml
  5. 디플로이먼트로 배포된 파드를 하나 삭제하고 이후 생성되는 파드를 관찰하라.
  6. kubectl delete pod deploy-jenkins-8688796664-75c4n $ kubectl get pod NAME READY STATUS RESTARTS AGE deploy-jenkins-8688796664-4t6t8 1/1 Running 0 5s deploy-jenkins-8688796664-klpnr 1/1 Running 0 74s deploy-jenkins-8688796664-ldx7r 1/1 Running 0 74s
  7. scale 명령을 사용해 레플리카 수를 5개로 정의한다.
  8. kubectl scale deployments deploy-jenkins --replicas=5

secret 저장소를 활용한 mysql 패스워드 설정

CI/CD의 과정에 따라 민감정보를 전달하면 불필요한 사람들에게 정보가 전달될 가능성이 있다. 따라서 배포하는 시점에 비밀정보를 전달할 수 있는 방법에 대해 익혀보자.

mysql 패스워드를 구성할 secret을 생성한다.

  • 파일 이름: 키 (passwd)
  • 파일 내용: 값 (test1234)
echo -n test1234 > passwd # 패스워드 관련 파일 생성
kubectl create secret generic mysql-pw --from-file=passwd # 앞서 생성한 파일을 사용해 시크릿 저장소를 구성한다.
kubectl get secret mysql-pw -o yaml # 잘 생성되었는지 출력

data의 부분에 passwd가 채워져 있는 모습이다.

apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: "2022-09-25T05:51:53Z"
  name: mysql-pw
  namespace: default
  resourceVersion: "6447"
  uid: 5223982d-1880-4276-a54a-5de6e02b92a4
type: Opaque
data:
  passwd: dGVzdDEyMzQ= # base64 encode

안전하게 보관하기 위해 etcd 데이터베이스를 디스크 암호화를 하는 것을 추천

echo dGVzdDEyMzQ=  | base64 -d # test1234

쿠버네티스에서 파드에 시크릿을 환경 변수로 전달하는 방법을 찾으면 됨

https://kubernetes.io/ko/docs/concepts/configuration/secret/#시크릿을-환경-변수-형태로-사용하기

echo -n test1234 > passwd # 패스워드 관련 파일 생성
kubectl create secret generic mysql-pw --from-file=passwd # 앞서 생성한 파일을 사용해 시크릿 저장소를 구성한다.
kubectl get secret mysql-pw -o yaml # 잘 생성되었는지 출력

cat <<EOF > mysql-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: mysql
spec:
  containers:
  - name: mysql
    image: mysql
    env:
      - name: MYSQL_ROOT_PASSWORD # 컨테이너 개발자가 정한다.
        valueFrom:
          secretKeyRef:
            name: mysql-pw # 시크릿 이름
            key: passwd    # 키 이름
EOF

kubectl apply -f mysql-pod.yaml

잘 실행되는지 관찰하고

kubectl get pod -w

접속해보자.

mysql 명령어로 127.0.0.1에 있는 mysql 서버로 접속한다. 이때 사용하는 유저는 root고 패스워드를 사용해 인증하겠다. 나올 때 exit 명령을 실행한다.

kubectl exec -it mysql -- mysql -h 127.0.0.1 -u root -p

쿠버네티스 시스템 컴포넌트

쿠버네티스 시스템에 필요한 파드들은 kube-system이라는 네임스페이스에서 동작 중이다. 우리가 사용하는 네임스페이는 default 네임스페이스다.

$ kubectl get pod -n kube-system
NAME                                       READY   STATUS    RESTARTS   AGE
calico-kube-controllers-58dbc876ff-g58tn   1/1     Running   2          3d17h
canal-gjbhw                                2/2     Running   0          40m
canal-vmxzc                                2/2     Running   0          40m
coredns-584f54878-dmj2q                    1/1     Running   0          3d17h
coredns-584f54878-ftz24                    1/1     Running   0          3d17h
etcd-controlplane                          1/1     Running   0          3d17h
kube-apiserver-controlplane                1/1     Running   2          3d17h
kube-controller-manager-controlplane       1/1     Running   3          3d17h
kube-proxy-d454k                           1/1     Running   0          3d17h
kube-proxy-vk72j                           1/1     Running   0          3d17h
kube-scheduler-controlplane                1/1     Running   3          3d17h

이 파드들은 kubelet에 의해 강제로 올라간 파드로 static pod라고 불린다. /etc/kubernetes/manifests/ 에 가면 다양한 yaml 파일이 있는데 파드에 대한 yaml 파일이다.

$ ls /etc/kubernetes/manifests/
etcd.yaml  kube-apiserver.yaml  kube-controller-manager.yaml  kube-scheduler.yaml

스태택 파드는 단순히 파일만 있으면 자동으로 구성되기 때문에 apply는 안해도된다. nginx yaml 파일을 하나 생성해보고 관찰해보자.

cat <<EOF > /etc/kubernetes/manifests/nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-static
  namespace: kube-system # 여기에 원하는 네임스페이스를 입력
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80
EOF

파드를 확인하면 다음과 같이 올라와 있다.

$ kubectl get pod -n kube-system
NAME                                       READY   STATUS    RESTARTS   AGE
calico-kube-controllers-58dbc876ff-g58tn   1/1     Running   2          3d17h
canal-gjbhw                                2/2     Running   0          46m
canal-vmxzc                                2/2     Running   0          46m
coredns-584f54878-dmj2q                    1/1     Running   0          3d17h
coredns-584f54878-ftz24                    1/1     Running   0          3d17h
etcd-controlplane                          1/1     Running   0          3d17h
kube-apiserver-controlplane                1/1     Running   2          3d17h
kube-controller-manager-controlplane       1/1     Running   3          3d17h
kube-proxy-d454k                           1/1     Running   0          3d17h
kube-proxy-vk72j                           1/1     Running   0          3d17h
kube-scheduler-controlplane                1/1     Running   3          3d17h
nginx-static-controlplane                  1/1     Running   0          26s

서비스

노드 포트 구성하기

gasbugs/http-go:v2.0 이미지를 사용해 디플로이먼트를 구성하고 이것을 외부로 노출시키는 노드포트 서비스를 구성하자.

kubectl delete all --all # 모든 자원 삭제

cat <<EOF > http-go-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: http-go
spec:
  replicas: 3
  selector:
    matchLabels:
      app: http-go
  template:
    metadata:
      labels:
        app: http-go
    spec:
      containers:
      - name: http-go
        image: gasbugs/http-go:latest
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: http-go
spec:
  type: NodePort # 노드에 포트를 오픈
  selector:
    app: http-go
  ports:
    - protocol: TCP
      port: 80         # 서비스할 포트
      targetPort: 8080 # 컨테이너 포트
EOF

kubectl apply -f http-go-deploy.yaml

파드와 서비스가 잘 동작하는지 확인한다.

$ kubectl get pod   
NAME                       READY   STATUS    RESTARTS   AGE
http-go-7c5c7bb8fc-76q7n   1/1     Running   0          2s
http-go-7c5c7bb8fc-kfff9   1/1     Running   0          30s
http-go-7c5c7bb8fc-wqfcd   1/1     Running   0          51s
$ kubectl get svc
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
http-go      NodePort    10.96.96.209   <none>        80:31918/TCP   37s
kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP        3d18h

노드 포트를 테스트해보자.

curl 127.0.0.1:31918

클러스터 IP를 테스트해보자. 내부용 IP이기 때문에 파드를 사용해서 요청해야 한다.

kubectl exec http-go-7c5c7bb8fc-76q7n  -- curl 10.96.96.209
kubectl exec http-go-7c5c7bb8fc-76q7n  -- curl http-go.default # 서비스이름.네임스페이스이름

쿠버네티스 통신 방식 이해

인증서 및 키 위치 확인

쿠버네티스의 컴포넌트는 인증서를 사용해 통신을 수행한다.

$ ls /etc/kubernetes/pki/
apiserver-etcd-client.crt     apiserver-kubelet-client.key  ca.crt  front-proxy-ca.crt      front-proxy-client.key
apiserver-etcd-client.key     apiserver.crt                 ca.key  front-proxy-ca.key      sa.key
apiserver-kubelet-client.crt  apiserver.key                 etcd    front-proxy-client.crt  sa.pub

$ ls /var/lib/kubelet/pki/
kubelet-client-2022-09-21-12-48-29.pem  kubelet-client-current.pem  kubelet.crt  kubelet.key

$ cat ~/.kube/config # kubectl 명령을 쓰는 유저가 사용하는것

유저 만들기

openssl 명령을 사용하면 개인 키와 공개 키를 구성할 수 있으며 개인 정보를 더해 인증기관 요청을 수행할 수 있다.

openssl genrsa -out gasbugs.key 2048 # 개인키 만들기
openssl req -new -key gasbugs.key -out gasbugs.csr -subj "/CN=gasbugs/O=boanproject" # 공개키를 구성해 CSR(인증서 싸인 요청)을 구성, gasbugs 유저는 boanproject라는 그룹에 속함

# 인증기관은 CSR을 받아서 싸인을 수행한다. 그러면 crt 생성된다.
openssl x509 -req -in gasbugs.csr -CA /etc/kubernetes/pki/ca.crt \\
                                  -CAkey /etc/kubernetes/pki/ca.key \\
                                  -CAcreateserial -out gasbugs.crt -days 365 # 인증서 1년짜리

유저가 잘 생성되었는지 확인

$ ls gas*   
gasbugs.crt  gasbugs.csr  gasbugs.key

kubectl에 crt와 key를 등록하면 사용가능하다.

# 유저를 등록
kubectl config set-credentials gasbugs --client-certificate=gasbugs.crt \\
                                       --client-key=gasbugs.key

# 유저의 정보와 클러스터 정보를 결합해 컨텍스트를 생성 # user@cluster
kubectl config set-context gasbugs@kubernetes --cluster=kubernetes \\
                                           --namespace=office \\
                                           --user=gasbugs

# 현재 사용하는 컨텍스트 변경
kubectl config use-context gasbugs@kubernetes

# 파드 조회
kubectl get pod 

오류가 보이면 성공한 것이다. Forbidden은 권한이 없다는 의미로 로그인에는 성공한 것이다.

Error from server (Forbidden): pods is forbidden: User "gasbugs" cannot list resource "pods" in API group "" in the namespace "office"

RBAC를 활용해 권한 부여하기

https://kubernetes.io/docs/reference/access-authn-authz/rbac/

gasbugs에게 파드 조회 권한을 부여하려면 롤과 롤바인딩을 구성해야 한다.

  • 롤: 역할에 어떤 권한을 부여할지 정함
  • 롤바인딩: 유저를 어떤 롤과 연결하지 정함

관리자 권한으로 돌아와서 다음 명령을 실행한다.

kubectl config use-context kubernetes-admin@kubernetes # 관리자 권한으로 변경

kubectl create ns office # 네임스페이스 생성

cat <<EOF > pod-reader-rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: office
  name: pod-reader
rules:
- apiGroups: [""] # core 그룹은 ""와 같이 생략 가능
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: office
subjects: # 누구에게?
- kind: User
  name: gasbugs
  apiGroup: rbac.authorization.k8s.io
roleRef: # 어떤 권한을?
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io
EOF

kubectl apply -f pod-reader-rbac.yaml

다시 한 번 gasbugs의 권한으로 get 파드를 실행해보자.

# office 네임스페이스에서는 조회가 잘 된다.
$ kubectl get pod --context=gasbugs@kubernetes
No resources found in office namespace.

# default 네임스페이스는 조회가 안된다.
$ kubectl get pod --context=gasbugs@kubernetes -n default
Error from server (Forbidden): pods is forbidden: User "gasbugs" cannot list resource "pods" in API group "" in the namespace "default"

api 레퍼런스

https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24

시큐리티컨텍스트를 활용한 권한 축소

다음과 같이 securityContext 를 설정하면 root 권한이 아니라 일반 유저 권한이 할당된다.

cat <<EOF > security-context-demo.yaml
apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox:1.28
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
    securityContext:
      allowPrivilegeEscalation: false
EOF

kubectl apply -f security-context-demo.yaml

쉘로 접근해서 유저 권한을 확인한다.

kubectl exec -it security-context-demo -- sh

권한 축소되어 sh 권한을 취득한다 하더라도 상당히 축소된 기능만을 수행할 수 있다.

$ kubectl exec -it security-context-demo -- sh
/ $ id
uid=1000 gid=3000 groups=2000
/ $ echo 1234 > test.txt
sh: can't create test.txt: Permission denied
/ $ echo 1234 > /data/demo/test.txt
/ $ exit

CIS 벤치마크 점검

trivy와 같은 회사인 아쿠아시큐리티 개발함

cd ~
rm -rf kube-bench
rm -rf /usr/bin/kube-bench

# 릴리즈 파일 다운로드
wget <https://github.com/aquasecurity/kube-bench/releases/download/v0.6.3/kube-bench_0.6.3_linux_amd64.tar.gz> 
tar -xf kube-bench_0.6.3_linux_amd64.tar.gz
sudo mv kube-bench /usr/bin/

# 깃헙 프로젝트 다운로드
git clone <https://github.com/aquasecurity/kube-bench> 
cd kube-bench
kube-bench --config-dir `pwd`/cfg --config `pwd`/cfg/config.yaml
728x90
반응형

댓글