PengX2 SOP (Standard Operating Procedure)
이 문서는 PengX(U+XEN 2) 표준운영절차 (Standard Operating Procedure)를 기술합니다.
서버 관련
uxen 서비스 점검 사항
uxen node가 부팅된 후 서비스 점검 사항
uxen node와 uxeus는 restapi (port 80)을 사용하여 통신합니다. 따라서 node에 해당 daemon이 구동되어 있어야 정상적으로 작동합니다.
- 해당 노드에 ssh 접속 후 web server(nginx) daemon 구동 여부 확인 # ps –ef | grep nginx 만약 nginx가 구동되어 있지 않다면 구동시킵니다. # /etc/init.d/nginx start
- was(uxen api) daemon 상태 확인 # ps –ef | grep uxen 만약 uxen api가 구동중이지 않다면 구동시킵니다. uxenapi는 orchard 계정으로 실행해야 합니다. # su - orchard $ cd ~/uxen/scripts $ ./uxen_was.sh start
uxen 서버 Hang
uxen 서버가 hang 걸리거나 down, reboot 되었을때
갑작스럽게 uxen 서버가 hang 걸려 서비스가 불가능한 상태이라면 아래와 같은 확인이 필요합니다.
- 먼저 uxen 서버에 구동중인 VM의 상태를 확인합니다. 경우에 따라 uxen(dom0)의 문제가 발생하여도 vm(domU)은 정상적으로 구동중일 수 있습니다. 이 경우라면 uxen은 그대로 두고 관리자에게 연락하는것이 좋습니다.
- uxen서버와 vm 모두 정상적이지 않다면 서비스 정상화를 위해 uxen을 강제 reboot시도합니다. 그리고 가능하다면 console 화면에 나타난 message를 사진등으로 남겨두는 편이 좋습니다. 갑작스럽게 hang걸려있을 경우 관련 log가 서버에 기록되지 않을수 있습니다.
uxen 서버의 예상치 않은 hang(down), reboot의 원인은 다른 서버들과 마찬가지로 매우 많습니다. 그 원인을 모두 나열할 순 없지만 현재까지 밝혀진 해결 방법은 아래와 같습니다.
- system log 확인 # less /var/log/messages
- dom0 메모리(pagecache) 관련 문제 Call Trace: alloc_xenballoned_page, vfs_ioctl /var/log/messages에 위와 같은 message가 찍혀 있다면 Dom0의 메모리 관련한 이슈입니다. 주기적으로 dom0의 pagecache를 삭제하도록 추가해야 합니다. # crontab –e 0 * * * * sync && echo 1> /proc/sys/vm_drop_caches
- ocfs2 관련 문제 o2net: No longer connected to node /var/log/message에 위와 같은 message가 찍혀 있다면 ocfs2의 heartbeat threshold값을 보수적으로 높게 설정합니다. # vi /etc/default/o2cb O2CB_HEARTBEAT_THRESHOLD=91
uxen 노드간의 시간 동기화 문제
노드간의 시간이 맞지 않아 장애 추적등의 관리자 어려울때
uxen의 경우 ntp를 통하여 서버간 시간동기화하고 있습니다. 사내에 내부 ntp server를 운영중이라면 uxen의 ntp 서버를 아래와 같이 설정합니다. 아래의 경우 ntp서버의 ip가 192.168.0.10의 경우 예입니다.
- ntp 서버 설정 # vi /etc/ntp.conf Server 192.168.0.10 ntp.conf에 다른 Server 설정을 모두 삭제합니다.
- ntp 적용, 데몬 재시작 # /etc/init.d/ntp stop # ntpdate –s 192.168.0.10 -- 즉시 ntp 적용 # /etc/init.d/ntp start -- 데몬 재시작
- 다른 노드와 비교하여 시간 확인 # date
HA 실패
HA가 실패하여 VM들이 다른노드에서 올라오지 않는 경우
해당 클러스터 존에 VM들이 이전될 유휴 자원(memory)이 있어야 정상적으로 HA됩니다. 따라서 유휴자원 이 부족할 경우 몇몇 VM은 실패할 수 있습니다.
- 각 node에 접속하여 구동중인 vm list를 확인합니다. Ha가 실패한 vm들을 추려냅니다. # xl list ha 실패한 vm들을 구동가능한 (유휴자원이 충족되는) node에서 구동합니다. uxeus에서 실행합니다.
- Vnc port 충돌 해결 해당 클러스터 존에 유휴자원이 충분하였는데 실패한 경우, 이미 선점된 vncport 로 vm을 ha해 문제되는 경우가 많습니다. 이럴 경우 db에 접속해 문제가 되는 vm의 vncport의 sync를 맞춰줘야 합니다. 방법은 기타 VM 관련 문제의 해결 방법과 동일 합니다.
VM 서버 관련
VM 구동 후 서비스 점검 사항
VM 구동 후 서비스 점검 사항
- 할당한 disk, vif들은 정상적으로 구동되었는지 fdisk -l, ifconfig -a등의 명령으로 확인합니다.
- dmesg등 시스템로그에 특이사항이 없는지 확인합니다.
- VM에 구동한 서비스가 정상적인지 확인합니다.
VM 구동 실패시
VM 구동이 실패 하였을 때
VM 구동이 실패하는 원인은 모두 나열하기 어려울만큼 매우 다양합니다. 아래의 방법으로도 해결되지 않는다면, 아이오차드에게 기술지원을 요청하시길 바랍니다.
- 스토리지 연결 확인 : 노드의 reboot후, 어떤 문제로 인해 스토리지가 제대로 연결(mount)되지 않았다면 VM 구동이 실패합니다. uxen2에서는 스토리지가 /data에 마운트되어야합니다. 아래의 명령으로 확인할 수 있습니다. # mount | grep /data Mount 되어있지 않다면 uxen 구동 후 스토리지 점검사항을 참고하시길 바랍니다.
- vm image 유무 확인 : 스토리지를 Localdisk로 사용중이라면 해당노드에 vm image가 없어서 실패할 수도 있습니다. 해당 위치에 image파일이 있는지 확인합니다. # ls –alR /data/domu/[vm-uuid]
- 기존 vm의 vnc port와 충돌 : 기타 vm 구동 관련 문제를 참고하시길 바랍니다.
vm 리부팅 후 추가 디스크 인식 실패
VM에 Live로 추가 디스크 할당, 정상인식하고 사용한뒤 vm 리부팅후 추가 디스크가 보이지 않을때
- db에 디스크 정보 확인 db에 디스크 정보 확인 # python ~orchard/uxen/manage.py dbshell –settings=settings.production uxenapi_production=>select * from pengx_disk where vm_uuid=‘[vm-uuid]’; -- disk의 개수확인
- vm 종료 후 구동 db의 vm disk 개수와 vm의 실제 disk가 일치한다면 해당 vm을 종료후 다시 구동하여 확인합니다. Live로 추 가된 disk의 정보가 xen에 반영이 되지 않아, 발생된 문제로 vm의 정상 종료후 구동하면 정상적으로 인식됩니다.
vm migration 실패 시
VM Migration이 실패 하였을때
- 옮겨갈 Node(target node)에 VM의 자원만큼 유휴자원이 있어야합니다. VM의 메모리양만큼 유휴메 모리가 있는지 확인합니다. target-node# xl info | grep free_mem
- Source Node(옮겨질 VM있는 node)에서 Target Node(옮겨갈 node)로 root ssh 접속 확인. migration시 root 유저로 public key를 이용하여 ssh 접속을 시도합니다. ssh 시도시 password를 물어본다면 public key를 확인합니다. source-node# ssh root@[target node] root@[target node]’s password: -- 패스워드를 물어본다면 source-node# cat /root/.ssh/id_rsa.pub ssh-rsa AAAABB#NzaC1yc#EAAQA....... target-node# cat >> /root/.ssh/authorized_keys -- 위의 public 키를 복사하여 붙힘
- target node의 ssh 설정 확인 target-node# vi /etc/ssh/ssh_config StrictHostKeyChecking no
- vncport 충돌 확인 # grep vncdisplay /data/domu/[vm-uuid]/current/[vm-uuid].cfg vncdisplay = [vnc-port] target-node# ps –ef | grep qemu | awk ‘{print $16,$18}’ | grep pass | awk –F”,|:| “ ‘{print $1,$3}’ | sort –n –k2 [vm-uuid] [vnc-port] -- 옮겨갈 node에 해당 vncport가 띄어져 있는지 확인 target-node# python ~orchard/uxen/manage.py dbshell –settings=settings.production uxenapi_production=> select pm.name, vm.uuid, vm.vnc_port from pengx_pm pm, pengx_vm vm where pm.uuid=vm.pm_uuid and pengx.name = ‘[nodehostname]’ -- sql문으로 db의 vncport 값 확인 uxenapi_production=> update pengx_vm set vnc_port=[vnc-port] where uuid=‘[vm-uuid]’ -- 해당 vncport 값을 넣어 db와 sync 맞춤
vm disk image 마운트
vm 디스크 이미지를 dom0에 마운트하여 사용이 필요한 경우
vm이 네트워크에 연결되지 않았는데 필요한 파일을 다운받아야 할 때, vnc 및 ssh 접속 모두 되어지지 않은 상태에서 점검이 필요할 때 디스크 이미지를 dom0에 마운트하여 사용할 수 있다.
vm이 구동중인 상태인지 확인(종료상태 확인) # xl list
qemu-nbd 명령어를 이용하여 vm disk image를 nbd device에 연결
# modprobe nbd # qemu-nbd -c /dev/nbd0 /data/domu/62adacb0-755f-4ad9-bc02-70fadd169152
kpartx 명령어를 이용하여 파티션 테이블에 장치를 추가 # kpartx -av /dev/nbd0 add map nbd0p1 (254:1): 0 20011008 linear /dev/nbd0 2048 add map nbd0p2 (254:2): 0 954370 linear /dev/nbd0 20015102 add map nbd0p5 : 0 954368 linear 254:2 2
추가된 파티션 테이블에 장치를 마운트 # mkdir /image # mount /dev/mapper/nbd0p1 /image
작업이 끝난 후 disconnect 작업 진행 # umount /image # kpartx -dv /dev/nbd0 del devmap : nbd0p5 del devmap : nbd0p2 # qemu-nbd -dv /dev/nbd0 /dev/nbd0 disconnected
기타 vm 구동 관련 문제
기타 다른 문제로 vm 구동 등이 실패한 경우
uxen에서는 vm의 console를 위해 각 vm마다 vnc port를 bind하여 연결합니다. 만약 uxen(db)에서 순차적으로 할당한 vnc port가 이미 사용중이라면 vm 구동이 실패하게 됩니다. 따라서 직접 xen cli등 U-XEUS(uxenapi)를 통하지 않고 vm 생성/구동하였을 경우, 문제가 발생할수 있습니다. 그리고 uxen2의 이전버전에서는 실질적인 사용 port와 uxen(db)에서 할당 port가 어긋나 vm 구동이 실패하는 경우가 빈번히 발생되었습니다. 이 경우 uxen의 patch로 해결이 가능하나, 아래의 과정으로 두 port간의 동기화를 진행 할 수 있습니다.
- 노드의 구동 Port 확인 : 아래 명령으로 구동중인 vm들의 uuid와 해당 vnc port를 확인할 수 있습니다. # ps –ef | grep qemu | awk ‘{print $16,$18}’ | grep pass | awk –F”,|:| “ ‘{print $1,$3}’ | sort –n –k2 21aaac9c-5b99-4229-9f83-0bf6b6dd8077 50 40b5b25e-c356-45fc-a287-7525c6438041 51 3a87b537-493d-4083-b5b6-64fc9276e693 52 70780615-290c-4822-93db-14c0d2fb2dc9 53 85345283-2345-4a59-b8bd-e64acfcd6858 55 d9c03045-ea57-42be-8a7d-1edd63fe591d 56 783561a5-7ab5-4064-bd0d-4def2f1ad838 57 2954d780-36df-45fc-98cc-e18b9c6b5ed5 58 ba375639-e3b0-4688-9a3f-9a3003ac95e6 59 1b1ad392-5a97-4bde-8ae4-e20ce9e4fa81 61
- uxen(db) 값 확인 : 아래의 [node_hostname]에 Node의 Hostname을 넣어줍니다. # su - orchard $ python ~orchard/uxen/manage.py dbshell –settings=settings.production uxenapi_production=> select vm.uuid, vm.vnc_port from pengx_vm vm, pengx_pm pm where vm.pm_uuid = pm.uuid and pm.name=‘[node_hostname]' order by vm.vnc_port;
- 1의 실제값들과 비교해봅니다. Vm 의 개수, uuid, vncport등을 확인합니다. 136a7cc4-5bda-4d90-ac30-02b442fca8c5 | 50 1ad9b1b5-c8ee-40d3-89b7-7666161c98f7 | 51 bf771ff0-20c3-4e77-a2ee-d2d67ea1effe | 52 69004eb8-167d-4354-948f-aabb91e7bd90 | 53 3de932b1-fb6b-4536-aff6-aab1e4e6256c | 54 c011b5e7-ae87-480d-b131-50c8f61e2bb0 | 55 b3521fa9-1e14-4ae0-ab62-008c3b717593 | 56 fa6e504b-1a33-4b05-8a8c-ce8945f44ea7 | 57 c1376d33-27fe-4261-ba9f-afc2136cf78a | 58 997111f3-40e9-4968-8f23-f74264151cc7 | 59 9a02594c-a4f1-4f58-9e02-76c911f7ba4f | 60
- uxen(db)값 변경 : 실제값과 비교하여 다른경우 DB값을 변경합니다. 일반적인 sql문과 동일합니다. 아래는 61로 변경한 예입니다. uxenapi_production=> update pengx_vm set vnc_port=61 where uuid=‘9a02594c-a4f1-4f58-9e02-76c911f7ba4f ‘
스토리지 관련
uxen 구동 후 스토리지 점검사항
uxen node가 부팅된후 스토리지 관련 점검 사항
- 스토리지 연결 확인 : 일반적으로 /data에 스토리지를 연결합니다. 그러나 경우 따라 iso공유를 위해 /data/iso, 속도를 위해 /data2로 연결후 Link를 거는등, 환경구성에 따라 다양합니다.
- Multipath 확인 : SAN을 사용하는 경우 일반적으로 Multipath로 구성하여 할당한 LUN을 LVM으로 묶어서 사용합니다. Multipath에는 문제가 없는지 확인합니다. # multipath -ll path별로 status값을 확인합니다. running이 아닌 failed된 path를 확인하시면 됩니다. 이중화 구성을 해놓은 상태라면 모두 fail이 아닌경우 서비스는 가능합니다. Failed되었을 경우 스토리지 담당자에게 확인을 부탁합니다. 할당된 LUN이 정상적으로 lvm에 나타나는지 확인합니다. # pvs PV VG Fmt Attr PSize PFree /dev/mapper/mpatha vg1 lvm2 a-- 1024.00g 0 /dev/mapper/mpathb vg1 lvm2 a-- 1024.00g 0 /dev/mapper/mpathc vg1 lvm2 a-- 1024.00g 0 /dev/mapper/mpathd vg1 lvm2 a-- 1024.00g 0 /dev/mapper/mpathe vg1 lvm2 a-- 1024.00g 0 /dev/mapper/mpathf vg1 lvm2 a-- 1024.00g 0 # vgs VG #PV #LV #SN Attr VSize VFree vg1 6 1 0 wz--n- 6.00t 0
- OCFS2 확인 : 공용스토리지로 사용하기 위해 VG로 묶은 Volume을 ocfs2로 구성합니다. 이 ocfs2의 연결에 문제가 없는지 확인합니다. # /etc/init.d/o2cb status Driver for "configfs": Loaded Filesystem "configfs": Mounted Stack glue driver: Loaded Stack plugin "o2cb": Loaded Driver for "ocfs2_dlmfs": Loaded Filesystem "ocfs2_dlmfs": Mounted # mounted 인지 확인 Checking O2CB cluster uxen-ocfs2: Online # online인지 확인 Heartbeat dead threshold = 91 Network idle timeout: 6000000 Network keepalive delay: 2000 Network reconnect delay: 2000 Checking O2CB heartbeat: Active # active인지 확인
- NAS 확인 : iso, template을 공유 하기 위해 nas(nfs)를 사용하였을 경우 해당 디렉토리가 정상적으로 mount되어 있는지 확인합니다.
- /data/template : Template 디렉토리
- /data/iso : 설치 이미지 ISO 디렉토리
- data2 디렉토리 : flash storage등 별도의 san등을 data2로 연결하였을경우, 해당 디렉토리도 mount 되어 있는지 확인합니다.
template 참조 문제로 vm 생성 실패
VM 생성시 template 참조 문제로 실패한 경우
Template dirctory에서 복사하여 vm을 생성하게 되는데 nas의 template이 mount 되지 않을 경우 vm 생성이 실패하게 됩니다.
- nas 연결 확인 # df –h -- 아래와 같이 template, iso 디렉토리가 mount 되어 있어야함 192.168.12.21:/data/template 2.8T 128G 2.7T 5% /data/template 192.168.12.21:/data/iso 2.8T 128G 2.7T 5% /data/iso
- mount # mount –t nfs4 192.168.12.21:/data/template /data/template # mount –t nfs4 192.168.12.21:/data/iso /data/iso
- 자동 mount 적용 # vi /etc/fstab 192.168.12.21:/data/template /data/template nfs4 _netdev 0 0 192.168.12.21:/data/iso /data/iso nfs4 _netdev 0 0
vm의 추가 디스크를 flash storage로 할당
VM의 추가 디스크를 Flash Storage로 할당하는 방법
vm에 추가 디스크 할당
- xeus에서 추가 디스크를 할당합니다. 그리고 할당한 vm의 uuid와 disk의 uuid를 확인합니다.
- 복사 진행을 위해 vm 종료 HDD storage에서 Flash storage로 disk 이미지를 복사하기 위해 vm을 종료해야만 합니다. xeus에서 vm을 종료 시킵니다.
- flashlink script 실행 해당 존의 node 1대(어느것이든)에만 아래의 명령을 수행합니다. # cd /data2 # ./flashlink.sh [vm_uuid] [disk_uuid] done # ls –al /data2/flash/[vm_uuid]/[disk_uuid].vhd
- vm 구동 및 disk 확인 직접 vm에 접속하여 disk를 확인합니다. # fdisk -l
vm image type 변환
생성된 vm image file의 type을 변환 (raw, qcow2, vhd)
uxen2에서는 image 변환에 대한 api가 따로 제공되지 않습니다. 따라서 관리자가 수동으로 직접 변환해야 합니다. vm image type을 변경하려면 실질적인 image type작업과 함께 uxen db에서의 설정값도 변경해야합니다.
- VM Shutdown : vm image type 변환전에 해당 vm을 종료해야합니다. 만약 해당 VM이 구동중이라면 전환된 image가 깨질 가능성이 큽니다. 해당 노드에서 아래의 명령으로 해당 image가 사용중인지 파악할 수 있습니다. (공용 스토리지를 사용한다면 모든 노드에서 확인해야합니다.) # lsof /data/domu/{vm_uuid}/current/{disk_uuid}.(raw,qcow2,vhd)
- backup : vm의 disk를 수정하는 작업이니만큼 신중히 작업해야합니다. 혹시 모를 변수들을 대비하여 backup을 진행하는 것이 좋습니다.
- Convert : qemu-img convert를 image를 변환를 변환합니다. 원본 file 자체가 변경되진 않으며, 변경된 output file은 따로 생성됩니다. 파일이 변환되면서 원본파일과 파일사이즈는 다를 수 있습니다. 아래는 vhd파일을 qcow2로 변환한 예입니다. 만일을 대비해 원본파일은 바로 지우지 않는것이 좋습니다. # cd /data/domu/{vm_uuid}/current # qemu-img convert -f vpc {disk_uuid}.vhd -O qcow2 {disk_uuid}.qcow2
- Image 확인 : 변환된 이미지가 정상적인지 qemu-img info로 간단히 파악할 수 있습니다. image file의 virtual size는 맞는지, checksum등은 깨지지 않았는지 확인합니다. # qemu-img info {disk_uuid}.qcow2
- UXEN DB값 변경 : uxen db에서 vm type을 변경합니다. # su - orchard $ cd ~/uxen $ python manage.py dbshell --settings=settings.production sql> select * from pengx_disk where uuid='{disk_uuid}'; sql> update pengx_disk set imgtype='qcow2' where uuid='{disk_uuid}';
- VM 구동 : xeus를 통하여 vm을 구동합니다. vm이 정상적으로 구동되는지, 변환후 vm내 partition은 깨지지 않았는지 제대로 살펴봐야합니다.
ocfs2 resize
- device rescan (모든 노드) # echo 1 > /sys/block/sdb/devices/rescan # echo 1 > /sys/block/sdc/devices/rescan # echo 1 > /sys/block/sdd/devices/rescan # echo 1 > /sys/block/sde/devices/rescan
- multipath devmap reload (모든 노드) # multipath -r
- ocfs2 resize (하나의 노드에서만) # tunefs.ocfs2 -S /dev/mapper/{LUN_UUID}
네트워크 관련
uxen 구동 후 네트워크 점검 사항
uxen node가 부팅된 후 네트워크 관련 점검 사항
네트워크 경우 간단히 ping 테스트로 확인이 가능합니다. 각 interface별로 gateway, node등에 ping test를 진행합니다.
- Bonding 확인 각 bridge interface들은 모두 active-backup으로 bonding되어 있습니다. 아래의 명령으로 bonding의 상태를 확인할 수 있습니다.아래의 경우 bond0의 예입니다. # cat /proc/net/bonding/bond0 Bonding Mode: fault-tolerance (active-backup) Currently Active Slave: eth4 -- 현재 active된 interface MII Status: up -- bond0 status , up되어 있는지 확인 Slave Interface: eth4 MII Status: up -- slave interface status Speed: 1000 Mbps Duplex: full Slave Interface: eth8 MII Status: up -- slave interface status Speed: 1000 Mbps Duplex: full 이중화구성으로 하나의 interface가 down되어도 서비스 가능하지만, 네트워크 담당자에게 알려주는것이 좋습니다.
- bridge 확인 open-vswitch로 bridge되어 있습니다. 아래의 명령으로 해당 포트와 bridge된 interface를 확인할 수 있습니다. # ovs-vsctl show Bridge "xenbr0" Port "xenbr0" Interface "xenbr0" type: internal Port "bond0" Interface "bond0" Bridge "xenbr1" Port "bond1" Interface "bond1" Port "xenbr1" Interface "xenbr1" type: internal Bridge "xenbr3" Port "bond3" Interface "bond3" Port "xenbr3" Interface "xenbr3" type: internal Bridge "xenbr2" Port "bond2" Interface "bond2" Port "xenbr2" Interface "xenbr2" type: internal MII Status: up -- slave interface status Speed: 1000 Mbps Duplex: full
온라인으로 vm에 vNIC 추가
운영중인 VM에 live로 Network Interface를 할당
현재 xeus에서 vm에 nic추가 기능이 없어 uxen node에 접속후 수동으로 작업해야 합니다.
- vm nic list 확인 # vi ~orchard/uxen/api/tests/include.sh SERVER=“http://localhost/api” -- 3번 라인의 서버 정보 수정 # ~orchard/uxen/api/tests/vm_interface_list.sh vm uuid: [vm-uuid 입력] <interfaces> <interface id="139"> <mac>00:16:3E:5E:FE:C7</mac> <name>wia7w6r5a</name> <ip></ip> </interface> </interfaces>
- vm의 nic 추가 # vi ~orchard/uxen/api/tests/vm_interface_create.sh <interface_name>xenbr2</interface_name> -- 추가하고자하는 bridge network 설정 # ~orchard/uxen/api/tests/vm_interface_create.sh vm uuid : [vm-uuid 입력] </interfaces>
- 추가된 nic 확인 정상적으로 추가되었는지 vm nic list 확인 # ~orchard/uxen/api/tests/vm_interface_list.sh vm uuid: [vm-uuid 입력]
- vm에서 nic 인식 확인 vm# ifconfig –a -- 추가된 interface 인식 확인
node의 네트워크 정보 변경 후 vm의 네트워크 작동 불가
Node의 bridge network 정보를 변경한뒤 vm의 네트워크가 작동되지 않을때
- Node의 bridge network를 아래와 같이 변경한 경우 # vi /etc/network/interfaces # /etc/init.d/networking restart 혹은 # ifdown xenbr[x] # ifup xenbr[x]
- VM 재부팅 위와 같이 network 정보를 변경하였을 경우 bridge network이 재시작되어 vm의 네트워크가 단절되게 됩니다. 이땐 Vm을 재부팅하면 정상적으로 네트워크가 작동합니다.
- Network 변경 아래와 같이 변경하면 bridge network의 재시작없이 (vm의 재구동없이) 변경 가능합니다. # ifconfig xenbr0 [ip_address] subnet [subnet] # route add default gw [gateway] -- gateway가 필요할 경우