Set-Based란?
셀렉터에서 Equality-Based는 레이블을 선택할 때 같은지 다른지 (=, ≠)만 확인하는데 반해 Set-Based (집합 기반) 셀렉터는 in, notin, exists 등과 같은 연산자(Operator)를 지원한다.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-replicaset
spec:
replicas: 3
selector:
matchLabels:
app: nginx-replicaset
template:
metadata:
name: nginx-replicaset
labels:
app: nginx-replicaset
spec:
containers:
- name: nginx-replicaset
image: nginx
ports:
- containerPort: 80
root@instance-1:~/kubernetes-sample/controller# kubectl apply -f replicaset-nginx.yaml
root@instance-1:~/kubernetes-sample/controller# kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-simple-pod 1/1 Running 0 24m
nginx-replicaset-dgbpl 0/1 ContainerCreating 0 5s
nginx-replicaset-l5vpd 0/1 ContainerCreating 0 5s
nginx-replicaset-n82xl 0/1 ContainerCreating 0 5s
root@instance-1:~/kubernetes-sample/controller# kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-simple-pod 1/1 Running 0 24m
nginx-replicaset-dgbpl 1/1 Running 0 9s
nginx-replicaset-l5vpd 1/1 Running 0 9s
nginx-replicaset-n82xl 1/1 Running 0 9s
Pod는 Label 기준으로 관리하므로 ReplicaSet과는 느슨하게 결합되어 있다.
아래 나와있다 시피 rc는 삭제되었지만 pod들은 삭제되지 않았음을 확인할 수 있다.
root@instance-1:~/kubernetes-sample/controller# kubectl delete replicasets.apps nginx-replicaset --cascade=false
warning: --cascade=false is deprecated (boolean value) and can be replaced with --cascade=orphan.
replicaset.apps "nginx-replicaset" deleted
root@instance-1:~/kubernetes-sample/controller#
root@instance-1:~/kubernetes-sample/controller# kubectl get pods,rc
NAME READY STATUS RESTARTS AGE
pod/kubernetes-simple-pod 1/1 Running 0 31m
pod/nginx-replicaset-5n7wg 1/1 Running 0 50s
pod/nginx-replicaset-86j28 1/1 Running 0 50s
pod/nginx-replicaset-s6l8m 1/1 Running 0 50s
root@instance-1:~/kubernetes-sample/controller# kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-simple-pod 1/1 Running 0 34m
nginx-replicaset-5n7wg 1/1 Running 0 3m27s
nginx-replicaset-86j28 1/1 Running 0 3m27s
nginx-replicaset-s6l8m 1/1 Running 0 3m27s
**nginx-replicaset-wpmm5 1/1 Running 0 4s
root@instance-1:~/kubernetes-sample/controller# kubectl get pods -o=jsonpath="{range .items[*]}{.metadata.name}{'\t'}{.metadata.labels}{'\n'}{end}"
kubernetes-simple-pod {"app":"kubernetes-simple-pod"}
nginx-replicaset-5n7wg {"app":"hello-nginx"}
nginx-replicaset-86j28 {"app":"nginx-replicaset"}
nginx-replicaset-s6l8m {"app":"nginx-replicaset"}
nginx-replicaset-wpmm5 {"app":"nginx-replicaset"}**
⇒ 해당 파드는 nginx-replicaset 이라는 레플리카세트에서 분리되어 별개의 pod가 되었기 때문에 새로운 nginx-replicaset-wpmm5 라는 파드를 하나 생성하였다.
Kubernetes가 stateless 앱을 배포할 때 사용하는 가장 기본적인 컨트롤러이다.
실행시켜야 할 파드 개수를 유지
앱을 배포할 때
롤링 업데이트 할 때
앱 배포 도중 잠시 멈췄다가 배포하기
앱 배포한 이후 이전 버전으로 롤백하기
⇒ 여러가지 기능이 있음
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx-deployment
# annotations:
# kubernetes.io/change-cause: version 1.10.1
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deployment
template:
metadata:
labels:
app: nginx-deployment
spec:
containers:
- name: nginx-deployment
image: nginx
ports:
- containerPort: 80
root@instance-1:~/kubernetes-sample/controller# kubectl get deploy,rs,rc,pods
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 3/3 3 3 15s
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-deployment-69cfdf5bc7 3 3 3 15s
replicaset.apps/nginx-replicaset 3 3 3 13m
NAME READY STATUS RESTARTS AGE
pod/kubernetes-simple-pod 1/1 Running 0 47m
pod/nginx-deployment-69cfdf5bc7-b7hgq 1/1 Running 0 15s
pod/nginx-deployment-69cfdf5bc7-jskvq 1/1 Running 0 15s
pod/nginx-deployment-69cfdf5bc7-xzrpt 1/1 Running 0 15s
pod/nginx-replicaset-5n7wg 1/1 Running 0 17m
pod/nginx-replicaset-86j28 1/1 Running 0 17m
pod/nginx-replicaset-s6l8m 1/1 Running 0 17m
pod/nginx-replicaset-wpmm5 1/1 Running 0 13m
업데이트하는 방법에는 크게 3가지가 있다.
- kubectl set
- 직접 컨테이너 이미지를 지정
kubectl edit
- 현재 파드의 설정 정보를 연 다음 컨테이너 이미지 정보 수정
Template에서 Container 이미지 정보 수정한 다음
- kubectl apply 명령어
kubectl set image deployment <<deployment 이름>> container_name=image:version 의 방법으로 해당 컨테이너의 이미지를 수정해볼 것이다.
root@instance-1:~/kubernetes-sample/controller# kubectl set image deployment/nginx-deployment nginx-deployment=nginx:1.9.1
root@instance-1:~/kubernetes-sample/controller# kubectl get deploy,rs,pods
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 3/3 3 3 11m
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-deployment-69cfdf5bc7 0 0 0 11m
**replicaset.apps/nginx-deployment-7b779c9596 3 3 3 43s**
replicaset.apps/nginx-replicaset 3 3 3 25m
NAME READY STATUS RESTARTS AGE
pod/kubernetes-simple-pod 1/1 Running 0 59m
**pod/nginx-deployment-7b779c9596-6mkdt 1/1 Running 0 33s
pod/nginx-deployment-7b779c9596-djzwq 1/1 Running 0 43s
pod/nginx-deployment-7b779c9596-r74tg 1/1 Running 0 24s**
pod/nginx-replicaset-5n7wg 1/1 Running 0 28m
pod/nginx-replicaset-86j28 1/1 Running 0 28m
pod/nginx-replicaset-s6l8m 1/1 Running 0 28m
pod/nginx-replicaset-wpmm5 1/1 Running 0 25m
## 변경내용 확인
## CHANGE-CAUSE에는 아무것도 기입되어 있지 않다.
## 추후 관리를 위해서는 기입해주는 것이 좋다.
root@instance-1:~/kubernetes-sample/controller# kubectl rollout history deployment nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
1 <none>
2 <none>
## revision=2 의 정보를 확인한다. **nginx:1.9.1 버전이다.**
root@instance-1:~/kubernetes-sample/controller# kubectl rollout history deployment nginx-deployment --revision=2
deployment.apps/nginx-deployment with revision #2
Pod Template:
Labels: app=nginx-deployment
pod-template-hash=7b779c9596
Containers:
nginx-deployment:
**Image: nginx:1.9.1**
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
## undo 명령으로 버전 롤백
root@instance-1:~/kubernetes-sample/controller# kubectl rollout undo deployment nginx-deployment
deployment.apps/nginx-deployment rolled back
## 확인하면 기존의 버전은 사라지고 롤백이 되고 있음을 확인할 수 있다.
root@instance-1:~/kubernetes-sample/controller# kubectl get deploy,rs,pods
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 3/3 3 3 17m
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-deployment-69cfdf5bc7 3 3 3 17m
replicaset.apps/nginx-deployment-7b779c9596 0 0 0 6m26s
replicaset.apps/nginx-replicaset 3 3 3 31m
NAME READY STATUS RESTARTS AGE
pod/kubernetes-simple-pod 1/1 Running 0 65m
**pod/nginx-deployment-69cfdf5bc7-9fgj2 1/1 Running 0 7s
pod/nginx-deployment-69cfdf5bc7-j2pt7 1/1 Running 0 9s
pod/nginx-deployment-69cfdf5bc7-xrhlp 1/1 Running 0 5s
pod/nginx-deployment-7b779c9596-6mkdt 0/1 Terminating 0 6m16s
pod/nginx-deployment-7b779c9596-djzwq 0/1 Terminating 0 6m26s
pod/nginx-deployment-7b779c9596-r74tg 0/1 Terminating 0 6m7s**
pod/nginx-replicaset-5n7wg 1/1 Running 0 34m
pod/nginx-replicaset-86j28 1/1 Running 0 34m
pod/nginx-replicaset-s6l8m 1/1 Running 0 34m
pod/nginx-replicaset-wpmm5 1/1 Running 0 31m
CHANGE-CAUSE : 변경 사항을 알 수 있는 버전 숫자 혹은 변경 내용을 메모할 수 있음
⇒ yaml template의 metadata.annotations 필드에 기입할 수 있다.
root@instance-1:~/kubernetes-sample/controller# kubectl apply -f deployment-nginx.yaml
root@instance-1:~/kubernetes-sample/controller# kubectl rollout history
error: required resource not specified
root@instance-1:~/kubernetes-sample/controller# kubectl rollout history deployment nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
2 <none>
**3 version 1.10.1**
root@instance-1:~/kubernetes-sample/controller# kubectl scale --replicas=5 deployment nginx-deployment
deployment.apps/nginx-deployment scaled
root@instance-1:~/kubernetes-sample/controller# kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-simple-pod 1/1 Running 0 79m
nginx-deployment-69cfdf5bc7-9fgj2 1/1 Running 0 14m
nginx-deployment-69cfdf5bc7-c7np8 1/1 Running 0 5s
nginx-deployment-69cfdf5bc7-j2pt7 1/1 Running 0 14m
nginx-deployment-69cfdf5bc7-qnljn 1/1 Running 0 5s
nginx-deployment-69cfdf5bc7-xrhlp 1/1 Running 0 14m
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
# namespace는 논리적으로 나눈 공간을 의미하며
# kube-system에 해당 Pod를 배치하겠다는 의미이다.
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
# matchLabels : = or != 만 확인함
selector:
matchLabels:
name: fluentd-elasticsearch
# update 전략 : RollingUpdate
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
containers:
- name: fluentd-elasticsearch
image: fluent/fluentd-kubernetes-daemonset:elasticsearch
# 환경 변수 설정
env:
- name: testenv
value: value
# memory, cpu에 제한(Limit)을 걸어두었다.
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
Check
확인해보니 fluentd-elasticsearch가 Master Node에는 올라오지 않았고 Worker Node에만 올라오는 것을 확인할 수 있었다.
⇒ kubectl get ds <<ds_name>> -o yaml 이런 식으로 default로 설정되어 있는 daemonset들과 내가 적용하려는 daemonset의 차이점이 무엇인지 확인하는 방법으로 toleration을 설정해줘야함을 확인하였다.
root@instance-1:~/kubernetes-sample/controller# kubectl get ds -n kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
calico-node 5 5 5 5 5 <none> 5d23h
**fluentd-elasticsearch 2 2 1 2 1 <none> 59s**
kube-proxy 5 5 5 5 5 kubernetes.io/os=linux 5d23h
nodelocaldns 5 5 5 5 5 <none> 5d23h
# taint가 걸려있는지 확인
root@instance-1:~/kubernetes-sample/controller# kubectl describe nodes instance-1 | grep -i taint
Taints: node-role.kubernetes.io/master:NoSchedule
## 정상적으로 모든 Node에 daemonset이 뜨는 것을 확인할 수 있다.
root@instance-1:~/kubernetes-sample/controller# kubectl get daemonset -n kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
calico-node 5 5 5 5 5 <none> 5d23h
fluentd-elasticsearch 5 5 5 5 5 <none> 3m48s
kube-proxy 5 5 5 5 5 kubernetes.io/os=linux 5d23h
nodelocaldns 5 5 5 5 5 <none> 5d23h
Stateful (상태가 있는)한 App을 관리하는데 사용한다.
파드들의 순서 및 고유성을 보장하는 API 오브젝트이다
( 각 파드의 독자성을 유지 )
⇒ Volume을 사용해서 특정 데이터를 저장한 후 Pod를 재시작했을 때 해당 데이터를 유지한다. 여러 개의 Pod 사이에 순서를 지정해서 실행되도록 할 수도 있다.
StatefulSet은 Pod를 각각 따로 관리해줘야 한다는 번거로움이 생길 수 있다. 또한 Service는 Headless Service로 생성해주어야 한다.
⇒ StatefulSet의 Pod Network ID를 유지하기 위해서이다.
⇒ Headless 서비스에는 IP가 할당되지 않지만 Domain Name을 사용하여 각 파드에 접근 가능
**When to use StatefulSet ?
1 안정적이고 고유한 네트워크 식별자가 필요한 경우
2 안정적이고 지속적인 스토리지를 사용해야 하는 경우
3 질서 정연한 포드의 배치와 확장을 원하는 경우
4 포드의 자동 롤링업데이트를 사용하기 원하는 경우**
# Headless 서비스 정의
apiVersion: v1
kind: Service
metadata:
name: nginx-statefulset-service
labels:
app: nginx-statefulset-service
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx-statefulset-service
---
# 스테이트풀셋 정의
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
# 셀렉터
selector:
matchLabels:
app: nginx-statefulset
# 설정할 서비스를 정의
# 쿠버네티스 클러스터 안에서 사용하는 도메인을 생성할 수 있다.
serviceName: "nginx-statefulset-service"
replicas: 3
template:
metadata:
labels:
app: nginx-statefulset
spec:
# 그레이스풀의 대기 시간 설정 ( 10초 )
# 실행 중인 프로세스를 바로 종료하지 않고 하던 작업 마무리 후 종료
terminationGracePeriodSeconds: 10
containers:
- name: nginx-statefulset
image: nginx
ports:
- containerPort: 80
name: web
Check
파드가 실행될 때는 작은 숫자부터 순서대로 실행 (0, 1, 2)되며 삭제될 때는 반대로 (2, 1, 0) 삭제된다.
root@instance-1:~/kubernetes-sample/controller# kubectl apply -f statefulset.yaml
service/nginx-statefulset-service created
statefulset.apps/web created
root@instance-1:~/kubernetes-sample/controller# kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-simple-pod 1/1 Running 0 146m
nginx-replicaset-5n7wg 1/1 Running 0 115m
web-0 1/1 Running 0 9s
web-1 1/1 Running 0 7s
web-2 1/1 Running 0 4s
root@instance-1:~/kubernetes-sample/controller# kubectl get statefulsets.apps
NAME READY AGE
web 3/3 19s
root@instance-1:~/kubernetes-sample/controller# kubectl delete -f statefulset.yaml
service "nginx-statefulset-service" deleted
statefulset.apps "web" deleted
root@instance-1:~/kubernetes-sample/controller# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
kubernetes-simple-pod 1/1 Running 0 150m
nginx-replicaset-5n7wg 1/1 Running 0 119m
web-0 0/1 Terminating 0 36s
web-1 0/1 Terminating 0 36s
web-0 0/1 Terminating 0 44s
web-0 0/1 Terminating 0 44s
web-1 0/1 Terminating 0 44s
web-1 0/1 Terminating 0 44s
⇒ 한꺼번에 생성되고 삭제됨을 확인할 수 있다.
실행된 후 종료해야 하는 성격의 작업을 실행해야 할 때 사용하는 컨트롤러
잡에서 하나 이상의 파드를 생성하고 지정된 수의 파드가 성공적으로 종료되도록 한다.
( Job Object를 하나 생성 후 Pod를 안정적으로 실행하고 완료하는 것 )
Job은 여러 Pod를 병렬(Parellel)로 실행시킬 수 있다.
# 배치 작업을 실행하는 v1 버전의 API
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: perl
# Perl 명령어로 원주율 계산
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
# 파드가 항상 성공으로 끝나게 한다.
# Never 외에 OnFailure도 가능
# OnFailure : 컨테이너가 정상 종료되지 않을 때 컨테이너를 다시 시작하도록 한다.
restartPolicy: Never
# Job 실행이 실패하였을 때 최대 몇번까지 재시작할 것인지 설정
# Default : 6
backoffLimit: 4
root@instance-1:~/kubernetes-sample/controller# kubectl apply -f job.yaml
job.batch/pi created
root@instance-1:~/kubernetes-sample/controller# kubectl describe job pi
Name: pi
Namespace: default
Selector: controller-uid=63eeab9f-6614-476c-8421-bbb383d2c135
Labels: controller-uid=63eeab9f-6614-476c-8421-bbb383d2c135
job-name=pi
Annotations: <none>
Parallelism: 1
Completions: 1
Start Time: Sat, 13 Feb 2021 11:42:59 +0000
Pods Statuses: 1 Running / 0 Succeeded / 0 Failed
Pod Template:
Labels: controller-uid=63eeab9f-6614-476c-8421-bbb383d2c135
job-name=pi
Containers:
pi:
Image: perl
Port: <none>
Host Port: <none>
Command:
perl
-Mbignum=bpi
-wle
print bpi(2000)
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 9s job-controller Created pod: pi-lk2qs
root@instance-1:~/kubernetes-sample/controller# kubectl logs pi-lk2qs
3.1415926
잡 병렬성 : 잡 하나가 몇 개의 파드를 동시에 실행할지를 결정하는 것
⇒ .spec.parallelism 필드에서 설정할 수 있다.
⇒ Default는 1이며 0으로 설정할 시 Job을 정지시킬 수 있다.
특정 시간을 지정해 잡 실행을 종료 : .spec.activeDeadlineSeconds
⇒ 지정된 시간에 해당 잡 실행을 강제로 끝내면서 모든 파드 실행도 종료한다.
⇒ Job의 종료 이유가 DeadlineExceeded로 표시된다.
kubectl delete job <<Job_Name>>
모든 작업을 관리하는 Job 하나를 사용하는 것이 좋다
⇒ Pod를 생성하는 Overhead가 크기 때문
작업 개수만큼의 Pod를 생성하는 것보다 Pod 하나가 여러 개의 작업을 처리하는 것이 좋다.
⇒ 이 또한 Overhead 때문
Work Quere를 사용한다면 Kafka나 RabbitMQ 같은 Queue 서비스로 Work Querue를 구현하도록 기존 프로그램이나 컨테이너를 수정해야 한다.
⇒ 워크 큐를 사용하지 않으면 그냥 기본 설정 그대로 컨테이너를 사용하므로 비효율적이다.ㄹ
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
# 매 1분마다 실행하도록 설정
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
imagePullPolicy: IfNotPresent
args:
- /bin/sh
- -c
- date; echo Hello from the kubernetes cluster
restartPolicy: OnFailure
Check
SCHEDULE : 스케줄링한 설정
SUSPEND : 이 크론잡이 정지되었는지 나타낸다.
root@instance-1:~/kubernetes-sample/controller# kubectl apply -f cronjob.yaml
cronjob.batch/hello created
# 현재 실행중인 크론잡
root@instance-1:~/kubernetes-sample/controller# kubectl get cronjobs
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
hello */1 * * * * False 0 <none> 15s
# 실행된 잡들
root@instance-1:~/kubernetes-sample/controller# kubectl get jobs
NAME COMPLETIONS DURATION AGE
hello-1613217480 1/1 3s 2m17s
hello-1613217540 1/1 3s 77s
hello-1613217600 1/1 2s 16s
pi 1/1 34s 17m
## 크론잡 삭제
root@instance-1:~/kubernetes-sample/controller# kubectl delete cronjobs.batch hello
cronjob.batch "hello" deleted
.spec.startingDeadlineSeconds
.spec.concurrencyPolicy
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello-concurrency
spec:
# 매 시간 1분마다 Job 실행
schedule: "*/1 * * * *"
# 600초로 설정
startingDeadlineSeconds: 600
# Job을 동시에 실행시키지 않음
# 처음에 실행했던 Job이 끝나야 다음 Job이 실행된다.
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster; sleep 6000
restartPolicy: OnFailure
root@instance-1:~/kubernetes-sample/controller# kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-concurrency-1613219580-xcslg 1/1 Running 0 85s
kubernetes-simple-pod 1/1 Running 0 5h7m
root@instance-1:~/kubernetes-sample/controller# kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-concurrency-1613219580-xcslg 1/1 Running 0 2m4s
hello-concurrency-1613219700-8gvkl 1/1 Running 0 4s
kubernetes-simple-pod 1/1 Running 0 5h7m
NAME READY STATUS RESTARTS AGE
hello-concurrency-1613219580-xcslg 1/1 Terminating 0 4m2s
hello-concurrency-1613219700-8gvkl 1/1 Terminating 0 2m2s
hello-concurrency-1613219760-892jd 1/1 Terminating 0 61s
hello-concurrency-1613219820-gzgv2 0/1 ContainerCreating 0 1s
root@instance-1:~/kubernetes-sample/controller# kubectl get cronjobs.batch,pods
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
cronjob.batch/hello-concurrency */1 * * * * True 1 108s 8m55s
NAME READY STATUS RESTARTS AGE
레플리카
우리 아이는 우리가 집에 있을 때 그의 곰을 카트 앞좌석에 앉힌다.
Posted on 2021년 2월 1일
우리 아이는 우리가 집에 있을 때 곰을 카트 앞좌석에 태운다. 그는 모든 사람들에게 이제 곰이 카트에 탄다고 알려준다. 우리가 쇼핑할 때 사용했던 것처럼. 그는 심지어 여러분의 집 주변을 걸어다니기도 하고, 장난감을 고르고, 카트에 넣기도 한다.
레플리카 순위
레플리카