libvirt: Guest migration

출처 : https://libvirt.org/migration.html

호스트 간 게스트 마이그레이션은 여러 가지 가능한 솔루션이 있는 복잡한 문제이며, 각 솔루션에는 장점과 단점이 있습니다. 하이퍼바이저 통합과 관리자 배포 모두의 유연성을 극대화하기 위해 libvirt는 마이그레이션을 위한 몇 가지 옵션을 구현합니다.

네트워크 데이터 전송

마이그레이션 중에 사용되는 데이터 전송에는 하이퍼바이저의 자체 기본 전송 또는 libvirtd 연결을 통한 터널링의 두 가지 옵션이 있습니다.

하이퍼바이저 네이티브 전송

기본 데이터 전송은 해당 하이퍼바이저에 따라 암호화를 지원할 수도 있고 지원하지 않을 수도 있지만, 일반적으로 관련된 데이터 복사본 수를 최소화하여 계산 비용이 가장 낮습니다. 또한 네이티브 데이터 전송은 호스트를 배포할 때 관리자가 하이퍼바이저별 네트워크 구성 단계를 추가로 수행해야 합니다. 일부 하이퍼바이저의 경우, 여러 개의 동시 마이그레이션 작업을 허용하기 위해 방화벽에서 광범위한 포트를 개방해야 할 수도 있습니다.

최신 하이퍼바이저는 마이그레이션 연결의 암호화 및 인증을 위해 TLS를 지원하며, 이는 VIR_MIGRATE_TLS 플래그를 사용하여 활성화할 수 있습니다. qemu 하이퍼바이저 드라이버를 사용하면 /etc/libvirt/qemu.conf에 구성된 migrate_tls_force 노브를 통해 TLS를 강제로 사용할 수 있습니다.

libvirt tunnelled 전송

tunneled 데이터 전송은 libvirt RPC 프로토콜에 내장된 기능을 활용할 수 있으므로 항상 강력한 암호화를 수행할 수 있습니다. 그러나 터널링된 전송의 단점은 데이터가 libvirtd와 하이퍼바이저 간에 이동할 때 소스 호스트와 대상 호스트 모두에 추가 데이터 복사본이 발생한다는 것입니다. 이는 메모리 페이지를 빠르게 더럽히는 매우 큰 RAM 크기를 가진 게스트의 경우 더 큰 문제가 될 수 있습니다. 배포 측면에서 터널링된 전송은 일반적인 libvirtd 원격 액세스에 이미 필요한 것 이상의 추가 네트워크 구성이 필요하지 않으며, 여러 동시 마이그레이션 작업을 지원하기 위해 방화벽에서 단일 포트만 열어두면 됩니다.

참고: 비공유 스토리지 마이그레이션(VIR_MIGRATE_NON_SHARED_DISK), 다중 연결 마이그레이션(VIR_MIGRATE_PARALLEL) 또는 사후 복사본 마이그레이션(VIR_MIGRATE_POSTCOPY) 같은 특정 기능은 libvirt의 터널링을 사용할 때 사용할 수 없을 수 있습니다.

통신 제어 경로/흐름

가상 머신 마이그레이션은 관련된 두 호스트와 마이그레이션을 호출하는 애플리케이션(소스, 대상 또는 제3의 호스트에 있을 수 있음)의 긴밀한 조정이 필요합니다.

Managed direct 마이그레이션

Managed direct 마이그레이션을 사용하면 libvirt 클라이언트 프로세스가 마이그레이션의 다양한 단계를 제어합니다. 클라이언트 애플리케이션은 소스 및 대상 호스트 모두에서 libvirtd 데몬에 연결하고 인증할 수 있어야 합니다. 두 개의 libvirtd 데몬이 서로 통신할 필요는 없습니다. 마이그레이션 프로세스 중에 클라이언트 애플리케이션이 충돌하거나 libvirtd와의 연결이 끊어지면 마이그레이션을 중단하고 소스 호스트에서 게스트 CPU를 다시 시작하려고 시도합니다. 이 작업을 안전하게 수행할 수 없는 시나리오가 있을 수 있으며, 이 경우 게스트는 호스트 중 하나 또는 둘 다에서 일시 중지된 상태로 유지됩니다.

Managed peer to peer 마이그레이션

Peer to peer 마이그레이션을 사용하면 libvirt 클라이언트 프로세스는 소스 호스트의 libvirtd 데몬과만 통신합니다. 소스 libvirtd 데몬은 대상 호스트 libvirtd에 직접 연결하여 전체 마이그레이션 프로세스 자체를 제어합니다. 클라이언트 애플리케이션이 충돌하거나 libvirtd에 대한 연결이 끊어지는 경우에도 마이그레이션 프로세스는 완료될 때까지 중단 없이 계속 진행됩니다. 클라이언트가 소스에 연결하는 데 사용하는 자격 증명이 아니라 소스 libvirtd가 자체 자격 증명(일반적으로 루트)을 사용하여 대상에 연결하므로, 클라이언트가 대상에 직접 연결할 수 있지만 소스가 피어 투 피어 마이그레이션을 설정하는 데 연결할 수 없는 상황이 발생하는 경우가 흔하다는 점에 유의하세요.

Unmanaged direct 마이그레이션

Unmanaged direct 마이그레이션을 사용하면 libvirt 클라이언트나 libvirtd 데몬이 마이그레이션 프로세스를 제어하지 않습니다. 대신 하이퍼바이저의 over management service(있는 경우)에 제어 권한이 위임됩니다. libvirt 클라이언트는 하이퍼바이저의 관리 계층을 통해 마이그레이션을 시작할 뿐입니다. libvirt 클라이언트 또는 libvirtd가 충돌하는 경우 마이그레이션 프로세스는 완료될 때까지 중단 없이 계속 진행됩니다.

데이터 보안

마이그레이션 데이터 스트림에는 게스트 OS RAM의 전체 복사본이 포함되므로 마이그레이션 데이터 스트림을 스누핑하면 민감한 게스트 정보가 손상될 수 있습니다. 가상화 호스트에 여러 네트워크 인터페이스가 있거나 네트워크 스위치가 태그가 지정된 VLAN을 지원하는 경우 게스트 네트워크 트래픽을 마이그레이션 또는 관리 트래픽과 분리하는 것이 매우 바람직합니다.

일부 시나리오에서는 마이그레이션 데이터를 위한 별도의 네트워크조차도 충분한 보안을 제공하지 못할 수 있습니다. 이 경우 마이그레이션 데이터 스트림에 암호화를 적용할 수 있습니다. 하이퍼바이저가 자체적으로 암호화를 제공하지 않는 경우 libvirt 터널링 마이그레이션 기능을 사용해야 합니다.

오프라인 마이그레이션

오프라인 마이그레이션은 도메인의 비활성 정의(활성 상태일 수도 있고 아닐 수도 있음)를 전송합니다. 성공적으로 완료된 후 도메인은 소스 호스트에서 현재 상태로 유지되며 대상 호스트에서는 정의되어 있지만 비활성 상태가 됩니다. 오프라인 마이그레이션은 마이그레이션 전 훅을 실행하여 대상 호스트의 도메인 XML을 업데이트하므로 소스 호스트에서 virsh dumpxml을 실행한 다음 대상 호스트에서 virsh define을 실행하는 것보다 조금 더 영리합니다. 현재 오프라인 마이그레이션 중에는 공유되지 않는 스토리지 또는 기타 파일 기반 스토리지(예: UEFI 가변 스토리지)를 복사하는 것은 지원되지 않습니다.

마이그레이션 URI

게스트 마이그레이션을 시작하려면 클라이언트 애플리케이션에서 제어 흐름 및/또는 사용된 API의 선택에 따라 최대 3개의 URI를 지정해야 합니다. 첫 번째 URI는 가상 게스트가 현재 실행 중인 소스 호스트에 대한 libvirt 연결의 URI입니다. 두 번째 URI는 가상 게스트가 이동될 대상 호스트에 대한 libvirt 연결입니다(피어 투 피어 마이그레이션의 경우 클라이언트가 아닌 소스 관점에서 볼 수 있음). 세 번째 URI는 게스트 마이그레이션 방법을 제어하는 데 사용되는 하이퍼바이저별 URI입니다. 관리형 마이그레이션 플로우에서는 첫 번째와 두 번째 URI는 필수이고 세 번째 URI는 선택 사항입니다. 관리되지 않는 직접 마이그레이션 모드에서는 첫 번째 및 세 번째 URI가 필수이며 두 번째 URI는 사용되지 않습니다.

일반적으로 관리 애플리케이션은 첫 번째 및 두 번째 URI만 신경 쓰면 되며, 둘 다 일반적인 libvirt 연결 URI 형식입니다. 그러면 Libvirt는 대상 호스트의 구성된 호스트 이름을 조회하여 하이퍼바이저별 URI를 자동으로 결정합니다. 관리 애플리케이션이 세 번째 URI를 직접 제어하고자 하는 몇 가지 시나리오가 있습니다.

  1. 구성된 호스트 이름이 올바르지 않거나 DNS가 손상된 경우. 호스트의 호스트 이름이 공인 IP 주소 중 하나와 일치하지 않는 경우 libvirt는 잘못된 URI를 생성합니다. 이 경우 관리 애플리케이션은 IP 주소 또는 올바른 호스트 이름을 사용하여 하이퍼바이저별 URI를 명시적으로 지정해야 합니다.
  2. 호스트에 여러 네트워크 인터페이스가 있습니다. 호스트에 여러 개의 네트워크 인터페이스가 있는 경우 보안 또는 성능상의 이유로 마이그레이션 데이터 스트림이 특정 인터페이스를 통해 전송되는 것이 바람직할 수 있습니다. 이 경우 관리 애플리케이션은 사용할 네트워크와 연결된 IP 주소를 사용하여 하이퍼바이저별 URI를 지정해야 합니다.
  3. 방화벽은 사용 가능한 포트를 제한합니다. libvirt가 마이그레이션 URI를 생성할 때 하이퍼바이저별 규칙을 사용하여 포트 번호를 선택합니다. 일부 하이퍼바이저는 방화벽에서 단일 포트만 개방하도록 요구하는 반면, 다른 하이퍼바이저는 전체 포트 번호를 요구합니다. 후자의 경우 관리 애플리케이션은 로컬 방화벽 정책을 준수하기 위해 기본 범위를 벗어난 특정 포트 번호를 선택해야 할 수 있습니다.
  4. 두 번째 URI는 UNIX 전송 방법을 사용합니다. 이 고급 경우에는 libvirt가 migrateuri를 추측해서는 안 되며 UNIX 소켓 경로 URI: unix:///path/to/socket을 사용하여 지정해야 합니다.

구성 파일 처리

가상 머신에는 두 가지 유형이 있습니다. 임시 게스트는 실행 중인 동안에만 존재하며 디스크에 구성 파일이 저장되지 않습니다. 영구 게스트는 실행 중이 아닐 때에도 디스크에 구성 파일을 유지합니다.

기본적으로 마이그레이션 작업은 소스 또는 대상 호스트에 저장될 수 있는 구성 파일을 수정하려고 시도하지 않습니다. 구성 파일의 배포를 관리하는 것은 관리자 또는 관리 애플리케이션의 책임입니다(원하는 경우). /etc/libvirt 디렉터리는 호스트 간에 절대 공유해서는 안 된다는 점에 유의해야 합니다. 적용 가능한 몇 가지 일반적인 시나리오가 있습니다:

  • 중앙 집중식 구성 파일이 libvirt 외부의 공유 스토리지에 있는 경우. 클러스터 인식 관리 애플리케이션은 클러스터 파일 시스템에서 모든 마스터 게스트 구성 파일을 유지 관리할 수 있습니다. 게스트 시작을 시도할 때 클러스터 FS에서 구성을 읽고 영구 게스트 배포에 사용합니다. 마이그레이션의 경우 구성을 대상 호스트에 복사하고 원본에서 제거해야 합니다.
  • 중앙 집중식 구성 파일을 데이터베이스 외부에 저장합니다. 데이터 센터 관리 애플리케이션은 구성 파일을 전혀 저장하지 않을 수 있습니다. 대신 게스트가 부팅될 때 즉시 libvirt XML을 생성할 수 있습니다. 일반적으로 임시 게스트를 사용하므로 마이그레이션 중에 구성 파일을 고려할 필요가 없습니다.
  • libvirt 내부의 분산 구성. 각 게스트에 대한 구성 파일은 게스트가 실행 가능한 모든 호스트에 복사됩니다. 마이그레이션 시 기존 구성은 변경 사항이 있는 경우 업데이트하기만 하면 됩니다.
  • libvirt 내부의 애드혹 구성 관리. 각 게스트는 특정 호스트에 연결되며 마이그레이션이 거의 필요하지 않습니다. 마이그레이션이 필요한 경우 구성이 한 호스트에서 다른 호스트로 이동됩니다.

위에서 언급했듯이 libvirt는 기본적으로 마이그레이션 중에 구성 파일을 수정하지 않습니다. 이 동작에 영향을 주는 두 가지 플래그가 virsh 명령에 있습니다. undefinesource 플래그를 사용하면 마이그레이션이 성공한 후 소스 호스트에서 구성 파일이 제거됩니다. 영구 플래그는 마이그레이션 성공 후 대상 호스트에 구성 파일을 만들도록 합니다. 다음 표에는 가능한 모든 상태 및 플래그 조합에서의 구성 파일 처리가 요약되어 있습니다.

Before migrationFlagsAfter migration
Source typeSource configDest config–undefinesource–persistentDest typeSource configDest config
TransientNNNNTransientNN
TransientNNYNTransientNN
TransientNNNYPersistentNY
TransientNNYYPersistentNY
TransientNYNNPersistentNY (unchanged dest config)
TransientNYYNPersistentNY (unchanged dest config)
TransientNYNYPersistentNY (replaced with source)
TransientNYYYPersistentNY (replaced with source)
PersistentYNNNTransientYN
PersistentYNYNTransientNN
PersistentYNNYPersistentYY
PersistentYNYYPersistentNY
PersistentYYNNPersistentYY (unchanged dest config)
PersistentYYYNPersistentNY (unchanged dest config)
PersistentYYNYPersistentYY (replaced with source)
PersistentYYYYPersistentNY (replaced with source)

마이그레이션 시나리오

네이티브 마이그레이션, 클라이언트를 두 개의 libvirtd 서버로 마이그레이션

API 수준에서는 VIR_MIGRATE_PEER2PEER 플래그를 설정하지 않고 virDomainMigrate를 사용해야 합니다. 대상 libvirtd 서버는 기본 호스트명을 기반으로 마이그레이션을 위한 기본 하이퍼바이저 URI를 자동으로 결정합니다. 대체 네트워크 인터페이스를 통해 마이그레이션을 강제로 수행하려면 선택적 하이퍼바이저 특정 URI를 제공해야 합니다.

syntax: virsh migrate GUESTNAME DEST-LIBVIRT-URI [HV-URI]


eg using default network interface

virsh migrate web1 qemu+ssh://desthost/system
virsh migrate web1 xen+tls://desthost/system


eg using secondary network interface

virsh migrate web1 qemu://desthost/system tcp://10.0.0.1/

Xen, QEMU, VMware 및 VirtualBox 드라이버 지원

네이티브 마이그레이션, 클라이언트 간 및 peer2peer, 두 libvirtd 서버 간 마이그레이션

‘uri’ 매개변수에 libvirt URI 형식을 사용하여 VIR_MIGRATE_PEER2PEER 플래그가 설정된 virDomainMigrate를 실행합니다. 대상 libvirtd 서버는 기본 호스트 이름을 기반으로 마이그레이션을 위한 기본 하이퍼바이저 URI를 자동으로 결정합니다. 클라이언트가 대상에 연결하는 데 사용하는 것과 동일한 주소를 사용하여 액세스할 수 없거나 다른 암호화/인증 체계가 필요한 경우, 선택적 uri 매개변수는 소스 libvirtd가 대상 libvirtd에 연결하는 방법을 제어합니다. 이 방법을 사용하면 기본 마이그레이션 데이터에 대해 대체 네트워크 인터페이스를 강제로 사용할 수 없습니다.

이 모드는 virsh에서 호출할 수 없습니다.

QEMU 드라이버에서 지원

Tunnelled 마이그레이션, 클라이언트 및 피어2피어 두 libvirtd 서버 간 마이그레이션

‘uri’ 매개 변수에 libvirt URI 형식을 사용하여 VIR_MIGRATE_PEER2PEER 및 VIR_MIGRATE_TUNNELLED 플래그를 설정한 virDomainMigrate를 실행합니다. 대상 libvirtd 서버는 기본 호스트 이름을 기반으로 마이그레이션을 위한 기본 하이퍼바이저 URI를 자동으로 결정합니다. 클라이언트가 대상에 연결하는 데 사용하는 것과 동일한 주소를 사용하여 액세스할 수 없거나 다른 암호화/인증 체계가 필요한 경우, 선택적 uri 매개변수는 소스 libvirtd가 대상 libvirtd에 연결하는 방법을 제어합니다. 기본 하이퍼바이저 URI 형식은 전혀 사용되지 않습니다.

이 모드는 virsh에서 호출할 수 없습니다.

QEMU 드라이버에서 지원

네이티브 마이그레이션, 클라이언트에서 하나의 libvirtd 서버로의 마이그레이션

‘uri’ 매개 변수에 대해 하이퍼바이저별 URI 형식을 사용하여 VIR_MIGRATE_PEER2PEER 플래그가 설정되지 않은 virDomainMigrateToURI를 호출합니다. 대상 libvirtd 인스턴스는 전혀 사용하거나 요구하지 않습니다. 이는 일반적으로 하이퍼바이저에 대상에서 들어오는 마이그레이션 시도를 처리할 수 있는 자체 기본 관리 데몬이 있는 경우에 사용됩니다.

syntax: virsh migrate GUESTNAME HV-URI


eg using same libvirt URI for all connections

네이티브 마이그레이션, 두 libvirtd 서버 간 피어2피어

‘uri’ 매개변수에 libvirt URI 형식을 사용하여 VIR_MIGRATE_PEER2PEER 플래그가 설정된 virDomainMigrateToURI를 실행합니다. 대상 libvirtd 서버는 기본 호스트 이름을 기반으로 마이그레이션을 위한 기본 하이퍼바이저 URI를 자동으로 결정합니다. 이 방법을 사용하면 기본 마이그레이션 데이터에 대한 대체 네트워크 인터페이스를 강제할 수 없습니다. 대상 URI는 소스 libvirtd 자격 증명(소스에 연결할 때 클라이언트의 자격 증명과 반드시 같을 필요는 없음)을 사용하여 연결할 수 있어야 합니다.

syntax: virsh migrate GUESTNAME DEST-LIBVIRT-URI [ALT-DEST-LIBVIRT-URI]


eg using same libvirt URI for all connections

virsh migrate --p2p web1 qemu+ssh://desthost/system


eg using different libvirt URI auth scheme for peer2peer connections

virsh migrate --p2p web1 qemu+ssh://desthost/system qemu+tls:/desthost/system


eg using different libvirt URI hostname for peer2peer connections

virsh migrate --p2p web1 qemu+ssh://desthost/system qemu+ssh://10.0.0.1/system

QEMU 드라이버에서 지원

터널링 마이그레이션, 두 libvirtd 서버 간의 피어2피어

‘uri’ 매개변수에 libvirt URI 형식을 사용하여 VIR_MIGRATE_PEER2PEER 및 VIR_MIGRATE_TUNNELLED 플래그가 설정된 virDomainMigrateToURI를 실행합니다. 대상 libvirtd 서버는 기본 호스트 이름을 기반으로 마이그레이션을 위한 기본 하이퍼바이저 URI를 자동으로 결정합니다. 클라이언트가 대상에 연결하는 데 사용하는 것과 동일한 주소를 사용하여 액세스할 수 없거나 다른 암호화/인증 체계가 필요한 경우, 선택적 uri 매개변수는 소스 libvirtd가 대상 libvirtd에 연결하는 방법을 제어합니다. 기본 하이퍼바이저 URI 형식은 전혀 사용되지 않습니다. 대상 URI는 소스 libvirtd 자격 증명(소스에 연결할 때 클라이언트의 자격 증명과 반드시 같을 필요는 없음)을 사용하여 연결할 수 있어야 합니다.

syntax: virsh migrate GUESTNAME DEST-LIBVIRT-URI [ALT-DEST-LIBVIRT-URI]


eg using same libvirt URI for all connections

virsh migrate --p2p --tunnelled web1 qemu+ssh://desthost/system


eg using different libvirt URI auth scheme for peer2peer connections

virsh migrate --p2p --tunnelled web1 qemu+ssh://desthost/system qemu+tls:/desthost/system


eg using different libvirt URI hostname for peer2peer connections

virsh migrate --p2p --tunnelled web1 qemu+ssh://desthost/system qemu+ssh://10.0.0.1/system

QEMU 드라이버에서 지원

UNIX 소켓만 사용하는 마이그레이션

libvirt 데몬이 네트워크에 액세스할 수 없는 틈새 시나리오(예: 네트워크에 액세스할 수 있는 호스트의 제한된 컨테이너에서 실행), 관리 애플리케이션이 전송을 완벽하게 제어하고자 하는 경우 또는 동일한 호스트의 두 컨테이너 간에 마이그레이션할 때 모든 통신은 UNIX 소켓을 사용하여 수행할 수 있습니다. 여기에는 대상 데몬의 비표준 소켓 경로에 연결하거나, 하이퍼바이저의 통신 또는 NBD 데이터 전송을 위해 UNIX 소켓을 사용하는 것이 포함됩니다. 이 모든 것은 피어투피어 및 직접 마이그레이션 옵션 모두에 사용할 수 있습니다.

두 libvirt 데몬에서 모두 표시되는 동일한 경로를 나타내는 디렉토리로 /tmp/migdir을 사용하는 예시입니다. 이는 별도의 데몬을 실행하는 다른 컨테이너에 동일한 디렉터리를 바인드 마운트하거나 이러한 소켓에 대한 연결을 수동으로 포워딩(socat, netcat 또는 사용자 정의 소프트웨어 사용)하여 수행할 수 있습니다:

virsh migrate --domain web1 [--p2p] --copy-storage-all
  --desturi 'qemu+unix:///system?socket=/tmp/migdir/test-sock-driver'
  --migrateuri 'unix:///tmp/migdir/test-sock-qemu'
  --disks-uri unix:///tmp/migdir/test-sock-nbd

한 가지 주의할 점은 SELinux 지원 시스템에서는 하이퍼바이저가 연결할 모든 소켓에 적절한 컨텍스트가 있어야 하며, 이는 소켓을 생성하는 프로세스에서 생성 전에 선택해야 한다는 것입니다. 이는 일반적으로 setsockcreatecon{,raw}() 함수를 사용하여 수행됩니다. 일반적으로 system_r:system_u:svirt_socket_t:s0를 사용하면 되지만, 시스템의 SELinux 규칙과 설정을 확인하세요.

QEMU 드라이버에서 지원

디스크용 비공유 이미지를 사용한 VM 마이그레이션

Libvirt는 기본적으로 명시적으로 네트워크에 액세스하지 않는 디스크 이미지는 네트워크 파일 시스템 또는 원격 블록 스토리지를 통해 호스트 간에 공유될 것으로 예상합니다.

기본적으로 동일한 위치에 있을 것으로 예상되지만, virsh 마이그레이션에 –xml 인수를 사용하여 이미지의 적절한 경로가 포함된 업데이트된 도메인 XML을 제공함으로써 이를 수정할 수 있습니다.

하나 이상의 이미지가 로컬 스토리지에 있는 경우 libvirt는 마이그레이션 흐름의 일부로 이미지를 마이그레이션할 수 있습니다. 이 기능은 virsh 마이그레이션에 –copy-storage-all 플래그를 사용하여 활성화합니다. 또한 –migrate-disks 매개변수를 사용하면 실제로 마이그레이션해야 하는 디스크를 제어할 수 있습니다. 이 플래그를 사용하지 않으면 모든 읽기-쓰기 디스크가 마이그레이션됩니다.

대상에서 이미지는 사용자가 올바른 형식과 크기로 미리 생성했거나 대상 경로가 libvirt 스토리지 풀 내에 있는 경우 자동으로 생성되어야 합니다.

사용자가 각 디스크에 대한 이미지의 백업 체인에서 최상위 이미지만 마이그레이션하려는 경우 –copy-storage-inc를 대신 사용할 수 있습니다. 사용자는 무조건 이미지를 미리 만들어야 합니다.

로컬 고속 스토리지로 많은 I/O를 수행하는 게스트가 디스크 마이그레이션에 부담을 주지 않도록 하기 위해 –copy-storage-synchronous-writes 플래그를 사용하면 새로 쓰여진 데이터가 대상에 동기적으로 쓰여지도록 할 수 있습니다. 마이그레이션 중에 I/O 성능이 저하될 수 있습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

You May Also Like