72일차) 2025-04-11(쿠버네티스 리소스-pod, replicaset, deploymnet, namespace, service)
리소스
1. Pod
쿠버네티스 기본단위, 최소단위
label
리소스를 특정하기 위한 꼬리표
ex. 만약에 내가 똑같은 pod를 3개 띄웠다면, pod들의 이름은 다르기때문에 대상을 한꺼번에 특정하기 어렵다. 하지만 똑같은 label을 달아준다면 해당 레이블로 3개의 파드를 동시에 특정 가능하다. 쿠버네티스에서는 보통 label을 통해 리소스를 식별하는 편이다.
root@master:~# vi test-pod.yml
apiVersion: v1 kind: Pod metadata: name: test-pod labels: app: my-web spec: containers: - image: public.ecr.aws/docker/library/nginx:alpine name: test-pod-con |
root@master:~# kubectl apply -f test-pod.yml
root@master:~# kubectl describe pod test-pod
root@master:~/mani# kubectl delete pod test-pod
2. ReplicaSet (레플리카셋)
파드의 복제본, pod보다 한단계 상위개념, 버전관리에 반드시 필요한 리소스
스케일링 가능, selector를 통해 복제할 pod의 label을 선택할 수 있음
(selector - 리소스를 연결하거나 선택하기 위한 조건식)
root@master:~# mkdir mani
root@master:~# cd mani/
root@master:~/mani# vi rep.yml
apiVersion: apps/v1 kind: ReplicaSet metadata: name: test-rep spec: replicas: 2 selector: matchLabels: app: my-web template: metadata: labels: app: my-web spec: containers: - image: public.ecr.aws/docker/library/nginx:alpine name: test-pod-con |
root@master:~/mani# kubectl apply -f rep.yml
실습1)
label이 app: my-httpd 이고 httpd:latest라는 이미지로 구성된 pod를 2개 띄우는 레플리카셋의 매니페스트 파일(rep-httpd.yml)을 만들어보세요.
root@master:~/mani# cp rep.yml rep-http.yml
root@master:~/mani# vi rep-http.yml
apiVersion: apps/v1 kind: ReplicaSet metadata: name: test-rep-httpd spec: replicas: 2 selector: matchLabels: app: my-httpd template: metadata: labels: app: my-httpd spec: containers: - image: public.ecr.aws/docker/library/httpd:latest name: test-pod-con |
root@master:~/mani# kubectl apply -f rep-http.yml
root@master:~/mani# kubectl get pod
- 레플리카셋의 pod를 삭제
# 내가 지운 pod는 삭제가 되지만, 새로운 pod가 생성된다.
# Desired state가 app: my-httpd라는 라벨을 달고 있는 파드가 2개이기때문. 내가 파드를 한개 삭제하는 순간, status(현재 상태)는 replicas=1 가 되기때문에, 하나를 더 만들어야 Desired state에 status가 수렴하므로, 하나를 더 만들었다.
- 두 레플리카셋을 삭제
root@master:~/mani# kubectl delete -f rep.yml
root@master:~/mani# kubectl delete -f rep-http.yml
root@master:~/mani# kubectl get rs,pod
- kubectl 명령 자동완성
apt-get install -y bash-completion
- 자동완성
echo 'source <(kubectl completion bash)' >>~/.bashrc
echo 'source <(kubeadm completion bash)' >>~/.bashrc
kubectl completion bash >/etc/bash_completion.d/kubectl
♨ xshell 재접속해야 반영 !
- kubectl 대신 k로 단축명령
ln -s /usr/bin/kubectl /usr/local/bin/k
root@master:~/mani# kubectl apply -f rep.yml
실습2)
k8s is easy라는 내용의 index.html 파일을 배포하는 아주 간단한 public.ecr.aws/docker/library/nginx:alpine 기반 app을 컨테이너 이미지로 만들어서 도커허브에 push한다음 쿠버네티스 로컬클러스터에 pod 3개로 배포해보세요. 도커허브가 안되면 ECR에 해보세요!
풀이)
1. 컨테이너 이미지 생성을 위한 Dockerfile 작성
root@master:~/mani# echo 'k8s is easy' > index.html
root@master:~/mani# vi Dockerfile
FROM public.ecr.aws/docker/library/nginx:alpine
WORKDIR /usr/share/nginx/html
COPY index.html index.html
2. 로컬에서 이미지 build & push
root@master:~/mani# docker build -t eitherwho/easy:1 .
root@master:~/mani# docker login
root@master:~/mani# docker push eitherwho/easy:1
3. 쿠버네티스에서 push한 이미지로 매니페스트 작성 및 apply
root@master:~/mani# cp rep-http.yml rep-easy.yml
root@master:~/mani# vi rep-easy.yml
apiVersion: apps/v1 kind: ReplicaSet metadata: name: easy-rep spec: replicas: 3 selector: matchLabels: web: easy-k8s template: metadata: labels: web: easy-k8s spec: containers: - image: oolralra/easy:1 name: easy-con |
root@master:~/mani# kubectl apply -f rep-easy.yml
root@master:~/mani# kubectl get pod -o wide
3. Deployment
배포를 위한 리소스
deployment는 특별한 기능이 없는 것처럼 보이지만, 레플리카셋을 여러개 포함할 수 있기때문에 우리가 배포하고자 하는 앱의 버전관리가 용이하다. 즉, rs-v1과 새로운 버전의 rs-v2가 있다면 rs-v1은 파드가 없는 껍데기만 남겨서 '형태만 유지'가 가능하다. 그래서 만약 rs-v2에 어떤 문제가 있다면, 다시 rs-v1으로 롤백도 가능하다.
cp rep-http.yml dep-http.yml
vi dep-http.yml
apiVersion: apps/v1 kind: Deployment #수정 metadata: name: my-dep spec: replicas: 2 selector: matchLabels: app: my-httpd template: metadata: labels: app: my-httpd spec: containers: - image: public.ecr.aws/docker/library/httpd:latest name: httpd-pod-con |
kubectl apply -f dep-http.yml
kubectl get pod -o wide
kubectl get deploy
kubectl get rs
# my-dep : deploy
# my-dep-746969d4c : ReplicaSet
# my-dep-746969d4c-v5bbf, vpjvh : pod
4. Namespace
비슷한 성질, 분류를 갖는 리소스들을 공간적으로 분리/격리하기 위한 리소스, 네임스페이스별로 접근제어도 가능
- 기존 상위 리소스까지 전부 삭제 (kubectl delete pod,rs,depoly --all)
kubectl delete deploy --all
kubectl delete rs --all
kubectl delete pod --all
- test-ns라는 이름을 갖는 네임스페이스 생성
kubectl create ns test-ns
- test-ns에 apply
kubectl apply -f dep-http.yml -n test-ns
- 리소스가 보이지 않음
kubectl get pod
- 우리가 네임스페이스 명시하지 않으면 기본 네임스페이스(default)에 명령이 들어감
kubectl get pod -n test-ns
- 모든 네임스페이스의 리소스를 다 보고 싶을땐
kubectl get pod --all-namespaces
kubectl get pod -A
- 모든 네임스페이스의 주요 리소스들을 다 보고 싶을때
kubectl get all -A
- 네임스페이스를 삭제시, 내부의 모든 리소스가 전부 삭제된다.
kubectl delete ns test-ns
- 이번에는 매니페스트로 ns를 정의
vi test-ns.yml
apiVersion: v1 kind: Namespace metadata: name: test-ns |
root@master:~/mani# kubectl apply -f test-ns.yml
- ns 조회
kubectl get ns
실습)
네임스페이스: lunch
디플로이먼트: lunch-dep
위 조건대로 생성하되, 하나의 yml파일로 매니페스트를 작성해야하고, 디플로이먼트는 lunch라는 네임스페이스에 존재해야합니다. 이미지는 public.ecr.aws/docker/library/nginx:alpine 로 하시고 레플리카 2개로 하세요.
따로 'kubectl create 같은 명령을 쓰진 마세요!'
root@master:~/mani# cp test-ns.yml test-lunch.yml
root@master:~/mani# vi test-lunch.yml
apiVersion: v1
kind: Namespace
metadata:
name: lunch
root@master:~/mani# kubectl apply -f test-lunch.yml
root@master:~/mani# cp dep-http.yml lunch-dep.yml
root@master:~/mani# vi lunch-dep.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: lunch-dep
namespace: lunch #metadata에서 ns 명시 가능
spec:
replicas: 2
selector:
matchLabels:
app: lunch-pod
template:
metadata:
labels:
app: lunch-pod
spec:
containers:
- image: public.ecr.aws/docker/library/nginx:alpine
name: lunch-con
root@master:~/mani# kubectl apply -f lunch-dep.yml
root@master:~/mani# kubectl get pod -n lunch
root@master:~/mani# kubectl delete -f lunch.yml
5. Service
작은 로드밸런서, 주로 pod 같은 리소스들을 대표 (부하분산 + 접속지점)
= pod들을 주로 외부에 '서비스'하는 리소스
서비스를 생성시 하나의 접속지점이 생성
이 서비스를 통해 모든 노드에 존재하는 파드에 트래픽을 인가할 수 있음
인가하는 기준은 labels를 통해 해당 파드를 특정하면 된다. 서비스는 다양한 종류(type)가 존재한다.
vi dep.yml
apiVersion: apps/v1 kind: Deployment metadata: name: my-dep spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: my-con image: public.ecr.aws/docker/library/nginx:alpine |
- apply하면 그림과 같은 deployment가 생성
kubectl apply -f dep.yml
5-1. ClusterIP
Service 리소스의 한 종류(=type)
= 클러스터 내에서만 유효한 Service
= 노드/클러스터 외부에서는 찾아갈 수 없는 서비스, 내부 테스트 용도로만 서비스를 만들고 싶을때
vi dep-svc.yml
apiVersion: v1 kind: Service metadata: name: svc-nginx spec: selector: app: nginx ports: - port: 80 targetPort: 80 |
kubectl apply -f dep-svc.yml
- svc를 좀 더 자세히 보기
kubectl describe svc svc-nginx
5-2. NodePort
Service 리소스의 한 종류(=type)
- 클러스터의 노드포트(nodePort, 30000-32767)로 사용자가 접근
- 해당 노드포트에 매칭된 서비스의 포트(port)로 연결
- 서비스는 selector를 통해 매칭된 파드의 포트(targetPort)로 접속
- dep-svc.yml의 타입을 NodePort로 변경
root@master:~/mani# vi dep-svc.yml
apiVersion: v1 kind: Service metadata: name: svc-nginx spec: selector: app: nginx type: NodePort # ClusterIP가 아닌 NodePort 타입,수정 ports: #3개의 포트를 명시 - nodePort: 30001 #추가 port: 80 targetPort: 80 |
root@master:~/mani# kubectl apply -f dep-svc.yml
root@master:~/mani# kubectl get svc svc-nginx
- 외부에서 노드로 통신만 된다면, 노드포트를 통해 pod로 접속 가능
♨ 3개의 포트(nodePort, port, targetPort)가 각각 어떤 대상인지 구분할 수 있어야 한다.
kubectl describe svc svc-nginx
kubectl delete svc --all
실습1)
서비스이름: svc-ipnginx
서비스포트: 80
노드포트: 32000
label = app: myipnginx
이미지: oolralra/ipnginx
파드수: 3개
네임스페이스: ip-ns
하나의 yml 파일을 통해 위와 같이 구성해보세요.
vi full.yml
# namespace
apiVersion: v1
kind: Namespace
metadata:
name: ip-ns
---
# service
apiVersion: v1
kind: Service
metadata:
name: svc-ipnginx
namespace: ip-ns
spec:
selector:
app: myipnginx
type: NodePort
ports:
- nodePort: 32000
port: 80
targetPort: 80
---
# replicaset
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: ipnginx
namespace: ip-ns
spec:
replicas: 3
selector:
matchLabels:
app: myipnginx
template:
metadata:
labels:
app: myipnginx
spec:
containers:
- image: oolralra/ipnginx
name: ipnginx-con
kubectl apply -f full.yml
풀이)
root@master:~/mani# vi hw1.yml
apiVersion: v1 kind: Namespace metadata: name: ip-ns --- apiVersion: apps/v1 kind: Deployment metadata: name: ip-dep namespace: ip-ns spec: replicas: 3 selector: matchLabels: app: myipnginx template: metadata: labels: app: myipnginx spec: containers: - name: ip-con image: oolralra/ipnginx --- apiVersion: v1 kind: Service metadata: name: svc-ipnginx namespace: ip-ns spec: selector: app: myipnginx type: NodePort ports: - nodePort: 32000 port: 80 targetPort: 80 |
실습2)
public.ecr.aws/docker/library/tomcat:10.1.40-jre11 tomcat 컨테이너이미지를 서비스포트 80번으로 지정해서 노드포트 31000으로 배포해보세요.
(tomcat에 index.jsp파일이 없으므로 에러페이지가 뜨는게 맞습니다~)
root@master:~/mani# cp full.yml full2.yml
root@master:~/mani# vi full2.yml
# service
apiVersion: v1
kind: Service
metadata:
name: svc-tomcat
spec:
selector:
app: mytomcat
type: NodePort
ports:
- nodePort: 31000
port: 80
targetPort: 8080
---
# replicaset
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: tomcat
spec:
replicas: 2
selector:
matchLabels:
app: mytomcat
template:
metadata:
labels:
app: mytomcat
spec:
containers:
- image: public.ecr.aws/docker/library/tomcat:10.1.40-jre11
name: tomcat-con
root@master:~/mani# kubectl apply -f full2.yml
root@master:~/mani# kubectl describe svc
Tomcat 404 Not Found는 정상일 수 있음
Tomcat은 루트 URL(/)에 대해 기본적으로 404 페이지를 보여줌, index.jsp가 없기 때문에 고양이 안뜸...
자율과제1)
서비스에서 selector를 통해 pod의 label을 선택할때, 해당 label이 존재하지 않는 경우의 svc를 describe해서 확인해보세요. svc와 deployment를 먼저 만드세요.
hw1.yml을 열어서 Service의 라벨을 수정
# app: myipnginx1이라는 라벨을 달고 있는 pod들이 없기때문에 svc의 describe상의 endpoints에도 뜨지 않는다.
자율과제2)
61.254.18.30:5000 에는 박찬민의 http 통신을 하는 사설 레지스트리가 존재합니다. 여기서 본인이름으로 된 이미지를 push 하고, 본인의 클러스터에 해당 이미지를 배포해보세요
1. Docker 이미지 만들기
vi Dockerfile
FROM nginx:alpine
RUN echo "<h1>Hello, I'm hoo!</h1>" > /usr/share/nginx/html/index.html
docker build -t hoo .
2. 사설 레지스트리에 push
docker tag hoo 61.254.18.30:5000/hoo:1
docker push 61.254.18.30:5000/hoo:1
3. 클러스터 노드 설정(모든 노드에서)
vi /etc/docker/daemon.json
{
"insecure-registries": ["61.254.18.30:5000"]
}
systemctl restart docker
4. k8s에 배포
vi deploy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
replicas: 2
selector:
matchLabels:
app: hoo
template:
metadata:
labels:
app: hoo
spec:
containers:
- name: con
image: 61.254.18.30:5000/hoo:1
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: hoo-svc
spec:
selector:
app: hoo
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30000
kubectl apply -f deploy.yml
5. 테스트
kubectl get pods
kubectl get svc
♨ ImagePullBackOff Err 해결
cat <<EOF >> /etc/containerd/config.toml [plugins."io.containerd.grpc.v1.cri".registry.mirrors."61.254.18.30:5000"] endpoint = ["http://61.254.18.30:5000"] [plugins."io.containerd.grpc.v1.cri".registry.configs."61.254.18.30:5000".tls] insecure_skip_verify = true EOF |
systemctl restart containerd
kubectl delete -f deploy.yml
kubectl apply -f deploy.yml
6. 레지스트리 확인
curl http://61.254.18.30:5000/v2/hoo/tags/list
- http://61.254.18.30:5001/ 브라우저에서 직접 확인
풀이)
- docker로 해당 레지스트리에 push를 하기 위해 docker 설정 변경
vi /etc/docker/daemon.json
{
"insecure-registries": ["61.254.18.30:5000"]
}
systemctl restart docker
- push할 이미지를 아무거나 pull
root@master:~/mani# docker pull public.ecr.aws/docker/library/nginx:alpine
- push를 하기위해 태그변경
docker tag public.ecr.aws/docker/library/nginx:alpine 61.254.18.30:5000/pcm:1
- push
docker push 61.254.18.30:5000/pcm:1
docker image rm 61.254.18.30:5000/pcm:1
# 푸쉬후 로컬에 있는 이미지는 삭제
docker run -dp 5959:80 --name test 61.254.18.30:5000/pcm:1
# 이미지 유효한지 테스트
# 테스트 목적: push된 이미지에는 문제가 없다라는걸 보장하고 싶음
이미지를 땡겨주는 주체가 누구인지? 어디서 땡겨오는지?
1. docker라는 런타임이냐? 아니면 containerd라는 런타임이냐? => containerd
우리가 매니페스트 파일을 apply 하면, api-server를 통해 노드의 kubelet이 pod를 생성하는데 그때 kubelet이 containerd라는 컨테이너런타음으로 pod에 들어갈 컨테이너를 생성. 따라서 containerd라는 컨테이너런타임이 레지스트리에 접근할 수 있어야 한다.
docker에서는 daemon.json에서 구성했듯
containerd 에서는 /etc/containerd/config.toml 에 내용을 추가해야한다.
2. 어디서 땡겨 오냐?
- 각 워커노드기때문에 containerd설정을 워커노드에서 해줘야한다.
- push된 이미지로 Deployment 매니페스트 작성
vi free.yml
apiVersion: apps/v1 kind: Deployment metadata: name: my-dep spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: my-con image: 61.254.18.30:5000/pcm:1 |
kubectl apply -f free.yml
cat <<EOF >> /etc/containerd/config.toml [plugins."io.containerd.grpc.v1.cri".registry.mirrors."61.254.18.30:5000"] endpoint = ["http://61.254.18.30:5000"] [plugins."io.containerd.grpc.v1.cri".registry.configs."61.254.18.30:5000".tls] insecure_skip_verify = true EOF |
systemctl restart containerd
kubectl delete -f free.yml
kubectl apply -f free.yml
'AWS Cloud School 8기 > 쿠버네티스' 카테고리의 다른 글
76일차) 2025-04-17(컨테이너의 헬스체크-readinessProbe, 리소스-StatefulSet, DaemonSet) (0) | 2025.04.17 |
---|---|
75일차) 2025-04-16(RBAC, 3A, Dynamic Provisioner) (0) | 2025.04.16 |
74일차) 2025-04-15(쿠버네티스 - pv, StorageClass, ConfigMap, Secret, wp/mysql/3tier) (0) | 2025.04.15 |
73일차) 2025-04-14(쿠버네티스 리소스 - service/metallb, ingress) (0) | 2025.04.14 |
71일차) 2025-04-10(쿠버네티스-k8s-tem, k8s 컴포넌트, 매니페스트) (0) | 2025.04.10 |