티스토리 뷰

시스템을 운용하다 보면 종종 Disk의 사용량이나 기타 문제로 인해 Disk Volume에 대한 대개체 혹은 그에 수반되는 Data Migration 이 필요 할 경우가 있다.

 

만약 Migration 대상 Data의 Size가 작거나 24x7 형태의 실시간성 서비스를 제공하지 않는 시스템이라면,

Data를 Migration 하는 동안 서비스를 중단시켜, 변경분 데이터의 발생을 막고 데이터의 무결성과 정합성을 보장 할 수 있을 것이다.

 

하지만 Migration 대상 데이터의 Size가 비교적 커서, Migration에 수반되는 서비스 중단시간이 길어 질 수 밖에 없다면, 해당 작업에 대한 비지니스적인 의사결정을 통해 리스크를 감수 해야한다.

시스템의 운영자로서 이러한 리스크를 최소화 하는 것은 책임과 의무 일 것이다.

 

이에 오늘은 rsync를 활용한 변경분 Data Migration 으로 Migration에 수반되는 서비스 중단시간 최소화에 대해 알아 보고자 한다.

 

 

 

가. 서비스 중단을 통한 일반적인 Data Migration

 

1. MySQL DB의 Data Volume 확인과 Disk Size 확인

 

[root@s-node01 ~]#
[root@s-node01 ~]# grep "datadir" /etc/my.cnf
datadir=/DATA/Volume1/mysql
[root@s-node01 ~]#
[root@s-node01 ~]#
[root@s-node01 ~]# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/mapper/VolGroup00-LogVol00
                      8.6G  5.4G  2.9G  66% /
/dev/sda1              99M   25M   70M  26% /boot
tmpfs                 249M     0  249M   0% /dev/shm
/dev/sdb1             5.0G  162M  4.6G   4% /DATA/Volume1
[root@s-node01 ~]#
[root@s-node01 ~]#
[root@s-node01 ~]#

 

 

2. MySQL DB 질의를 통한 Table 상태 및 Row Count 확인

 

[root@s-node01 ~]#
[root@s-node01 ~]# mysql -u helperchoi -p1234567 -D test_db -e "show tables;"
+-------------------+
| Tables_in_test_db |
+-------------------+
| test_table        |
| test_table2       |
+-------------------+
[root@s-node01 ~]#
[root@s-node01 ~]#
[root@s-node01 ~]# mysql -u helperchoi -p1234567 -D test_db -e "desc  test_table;"
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(10)     | NO   | PRI | NULL    | auto_increment |
| data  | varchar(50) | NO   |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
[root@s-node01 ~]#
[root@s-node01 ~]#
[root@s-node01 ~]# vi mysql_table_count.sh && chmod 755 mysql_table_count.sh
#!/bin/bash

echo "DATABASE SIZE (test_db) : `du -k /DATA/Volume1/mysql/test_db/ | awk '{print $1}'` KB"
echo
echo "TABLE COUNT1 (test_table) : "
mysql -u helperchoi -p1234567 -D test_db -e "select COUNT(*) from test_table"
echo

echo "TABLE COUNT2 (test_table2) : "
mysql -u helperchoi -p1234567 -D test_db -e "select COUNT(*) from test_table2"
echo
[root@s-node01 ~]#
[root@s-node01 ~]#
[root@s-node01 ~]# ./mysql_table_count.sh
DATABASE SIZE (test_db) : 1964 KB

TABLE COUNT1 (test_table) :
+----------+
| COUNT(*) |
+----------+
|    46540 |
+----------+

TABLE COUNT2 (test_table2) :
+----------+
| COUNT(*) |
+----------+
|     4200 |
+----------+

[root@s-node01 ~]#

 

 

3. MySQL DB 서비스 중지 및 Disk 정보 확인

 

[root@s-node01 ~]#
[root@s-node01 ~]# service mysqld stop
MySQL 를 정지 중:                                          [  OK  ]
[root@s-node01 ~]#
[root@s-node01 ~]#
[root@s-node01 ~]#
[root@s-node01 ~]#
[root@s-node01 ~]# fdisk -l | grep Disk
Disk /dev/sda: 10.7 GB, 10737418240 bytes
Disk /dev/sdb: 5368 MB, 5368709120 bytes
Disk /dev/sdc: 2147 MB, 2147483648 bytes
Disk /dev/sdd: 5368 MB, 5368709120 bytes
[root@s-node01 ~]#
[root@s-node01 ~]#
[root@s-node01 ~]#

 

 

4. dd 명령어를 활용한 Disk Dump Migration 수행

 

[root@s-node01 ~]#
[root@s-node01 ~]#
[root@s-node01 ~]# dd if=/dev/sdb1 of=/dev/sdd1 bs=4096
1309289+1 records in
1309289+1 records out
5362850304 bytes (5.4 GB) copied, 77.6072 seconds, 69.1 MB/s
[root@s-node01 ~]#
[root@s-node01 ~]#

 

 

5. Disk 대개체(교체) 수행 및 확인

 

[root@s-node01 ~]#
[root@s-node01 ~]# umount /dev/sdb1
[root@s-node01 ~]#
[root@s-node01 ~]#
[root@s-node01 ~]# mount -t ext3 /dev/sdd1 /DATA/Volume1
[root@s-node01 ~]#
[root@s-node01 ~]#
[root@s-node01 ~]# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/mapper/VolGroup00-LogVol00
                      8.6G  5.4G  2.9G  66% /
/dev/sda1              99M   25M   70M  26% /boot
tmpfs                 249M     0  249M   0% /dev/shm
/dev/sdd1             5.0G  162M  4.6G   4% /DATA/Volume1
[root@s-node01 ~]#
[root@s-node01 ~]#

 

 

6. MySQL DB 서비스 기동 및 Table ROW Count 확인을 통한 DB 검증

 

[root@s-node01 ~]#
[root@s-node01 ~]# service mysqld start
MySQL (을)를 시작 중:                                      [  OK  ]
[root@s-node01 ~]#
[root@s-node01 ~]#
[root@s-node01 ~]#
[root@s-node01 ~]#  mysql -u helperchoi -p1234567 -D test_db -e "show tables;"
+-------------------+
| Tables_in_test_db |
+-------------------+
| test_table        |
| test_table2       |
+-------------------+
[root@s-node01 ~]#
[root@s-node01 ~]#
[root@s-node01 ~]# ./mysql_table_count.sh
DATABASE SIZE (test_db) : 1964 KB

TABLE COUNT1 (test_table) :
+----------+
| COUNT(*) |
+----------+
|    46540 |
+----------+

TABLE COUNT2 (test_table2) :
+----------+
| COUNT(*) |
+----------+
|     4200 |
+----------+

[root@s-node01 ~]#

 

 

 

나. rsync Online Data Migration 수행을 통한 서비스 중단 최소화 및 변경분 Data Migration

 

상기 가. 에서 사용한 dd 명령어의 경우 변경분 데이터만 선택적으로 Copy 하는 것이 아닌 Disk 전체 Volume을 Dump 하는 방식으로 일반적인 Local Disk나 FC-SAN, iSCSI를 통해 할당 받은 Block Storage Device 에만 사용할 수 있다.

※ 아래와 같이 NAS(NFS) Volume에 대해서는 dd 명령을 수행 할 수 없음.

[root@CentOS6 /]#
[root@CentOS6 /]# dd if=/NAS1 of=/NAS2
dd: opening `/NAS2': Is a directory
[root@CentOS6 /]#
[root@CentOS6 /]# dd if=192.168.137.10:/DATA/Volume1 of=192.168.137.10:/DATA/Volume2
dd: opening `192.168.137.10:/DATA/Volume1': No such file or directory
[root@CentOS6 /]#
[root@CentOS6 /]#

 

만약 이전해야할 Data Volume의 Device가 dd 명령어를 사용할 수 없는 NAS(NFS) Volume 이라면 어쩔 수 없이 cp 명령등을 통해 File 단위의 Data Migration을 수행해야 하며,

 

어떠한 사유로 인해 데이터 이전을 수행중이던 cp 명령이 중단되거나, 이전된 파일의 무결성에 문제가 생겼을 경우 전체 데이터에 대한 일괄 이전을 재수행 해야만 하며,

 

별도의 File 검증로직을 포함한 Shell Script를 활용하지 않는 이상 cp 명령만으로는 변경분 데이터에 대한 선택적 이전을 수행하기 어렵다.

 

이러한 상황에서 아래와 같이 손쉽게 사용 할 수 있는 rsync 명령의 활용법에 대해 알아 보고자 한다.

 

 

1. MySQL DB Data 영역확인 및 대개체를 위한 NAS Volume Mount

 

[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]# cat /etc/my.cnf | grep "datadir"
datadir=/NAS1/mysql
[root@CentOS6 ~]#

[root@CentOS6 ~]#
[root@CentOS6 ~]# ls -l /NAS1/mysql
total 20532
-rwxr-xr-x 1 mysql mysql 10485760 Aug 25  2015 ibdata1
-rwxr-xr-x 1 mysql mysql  5242880 Aug 25  2015 ib_logfile0
-rwxr-xr-x 1 mysql mysql  5242880 May 11 10:58 ib_logfile1
drwxr-xr-x 2 mysql mysql     4096 May 11 10:58 mysql
srwxrwxrwx 1 mysql mysql        0 Aug 25  2015 mysql.sock
drwxr-xr-x 2 mysql mysql     4096 May 11 10:58 test
drwx------ 2 mysql mysql     4096 Aug  3 21:37 test_db
[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]# mount -t nfs 192.168.137.10:/DATA/Volume2 /NAS2
[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]# mount -l | grep nfs
sunrpc on /var/lib/nfs/rpc_pipefs type rpc_pipefs (rw)
192.168.137.10:/DATA/Volume1 on /NAS1 type nfs (rw,addr=192.168.137.10)
192.168.137.10:/DATA/Volume2 on /NAS2 type nfs (rw,addr=192.168.137.10)
[root@CentOS6 ~]#

[root@CentOS6 ~]#
[root@CentOS6 ~]# df -h
Filesystem                    Size  Used Avail Use% Mounted on
/dev/mapper/VolGroup-lv_root  6.7G  1.2G  5.1G  19% /
tmpfs                         246M     0  246M   0% /dev/shm
/dev/sda1                     485M   33M  427M   8% /boot
192.168.137.10:/DATA/Volume1  5.0G  162M  4.6G   4% /NAS1
192.168.137.10:/DATA/Volume2  5.0G  139M  4.6G   3% /NAS2
[root@CentOS6 ~]#

[root@CentOS6 ~]#

 

 

2. MySQL DB Table 구조 확인 및 Table Row Count  확인

 

[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]#  mysql -u helperchoi -p1234567 -D test_db -e "show tables;"
+-------------------+
| Tables_in_test_db |
+-------------------+
| test_table        |
| test_table2       |
+-------------------+
[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]# mysql -u helperchoi -p1234567 -D test_db -e "desc test_table"
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(10)     | NO   | PRI | NULL    | auto_increment |
| data  | varchar(50) | NO   |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]# mysql -u helperchoi -p1234567 -D test_db -e "desc test_table2"
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(10)     | NO   | PRI | NULL    | auto_increment |
| data  | varchar(50) | NO   |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]# /root/shell/mysql_table_count.sh
DATABASE SIZE (test_db) : 1964 KB

TABLE COUNT1 (test_table) :
+----------+
| COUNT(*) |
+----------+
|    46540 |
+----------+

TABLE COUNT2 (test_table2) :
+----------+
| COUNT(*) |
+----------+
|     4200 |
+----------+

[root@CentOS6 ~]#
[root@CentOS6 ~]#

 

 

3. 실시간 DB Transection 발생을 통한 테스트 환경 조성

※ 2개의 Test Table에 초당 80~200건의 동시 Insert Query 인입 처리

 

[root@CentOS6 /]#
[root@CentOS6 /]#
[root@CentOS6 /]# while [ 0 ]; do mysql -u helperchoi -p1234567 -D test_db -e "INSERT INTO test_table (data) VALUES ('`echo $RANDOM | md5sum`');"; done &
[1] 19024
[root@CentOS6 /]#
[root@CentOS6 /]# while [ 0 ]; do mysql -u helperchoi -p1234567 -D test_db -e "INSERT INTO test_table2 (data) VALUES ('`echo $RANDOM | md5sum`');"; done &
[2] 8437
[root@CentOS6 /]#
[root@CentOS6 /]#
[root@CentOS6 /]#
[root@CentOS6 /]# /root/shell/mysql_table_count.sh && sleep 1 && /root/shell/mysql_table_count.sh
DATABASE SIZE (test_db) : 4228 KB

TABLE COUNT1 (test_table) :
+----------+
| COUNT(*) |
+----------+
|    69903 |
+----------+

TABLE COUNT2 (test_table2) :
+----------+
| COUNT(*) |
+----------+
|    23522 |
+----------+

DATABASE SIZE (test_db) : 4236 KB

TABLE COUNT1 (test_table) :
+----------+
| COUNT(*) |
+----------+
|    69982 |
+----------+

TABLE COUNT2 (test_table2) :
+----------+
| COUNT(*) |
+----------+
|    23602 |
+----------+

[root@CentOS6 /]#
[root@CentOS6 /]#

 

 

4. 인입된 Insert Query에 의한 Table Row 확인

 

[root@CentOS6 /]#
[root@CentOS6 /]# mysql -u helperchoi -p1234567 -D test_db -e "select * from test_table" | tail -10
74173 e21ab6037259d7d3d647b5e491a60064  -
74174 0884c1543829b1ac2b1c1f5fc0894d68  -
74175 4e6423483146933217cbbe3422b9d39e  -
74176 3c63176b15ce3583d0f79464fe8bb253  -
74177 775c9ee1ad4e98da19929dbead72bf20  -
74178 ef46e78420cc26819696c2acd51bf7a4  -
74179 63006880a8ed1a03f1a552f1e518ef1a  -
74180 5890652ee963e4a688c1647b63cc315e  -
74181 fe550f536eff4e20e6e07276044969fc  -
74182 b5eda82b1f51eb063516a712a89b423b  -
[root@CentOS6 /]#
[root@CentOS6 /]#
[root@CentOS6 /]# mysql -u helperchoi -p1234567 -D test_db -e "select * from test_table2" | tail -10
28528 ac255841c6ad3ad4b5a58e52230eb609  -
28529 50a3850b19922c80acb5cc922ee680e6  -
28530 b99be706a7853c37ebf0552a3e2c36b2  -
28531 d88484e80c6436e6275858f3b2e6e293  -
28532 bb6763abfd49a13fbc2c539c22506909  -
28533 b3811d425579be05b2464de363ea8e98  -
28534 dc1b4e5562b8ad5396324fb617de05ad  -
28535 27854fd1dd4dde4bad64773f416d6a43  -
28536 545a4b98cd58cd69719555d6ea038413  -
28537 8e9187aae3d6d28346d4230402fcb7c9  -
[root@CentOS6 /]#
[root@CentOS6 /]#

 

 

5. MySQL DB에 실시간 Transection 이 발생하는 환경에서 Online Data Migration 수행

 

[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]# time rsync -avtz /NAS1/mysql /NAS2/
sending incremental file list
mysql/
mysql/ib_logfile0
mysql/ib_logfile1
mysql/ibdata1
mysql/mysql.sock
mysql/mysql/
mysql/mysql/columns_priv.MYD
mysql/mysql/columns_priv.MYI
mysql/mysql/columns_priv.frm
mysql/mysql/db.MYD
mysql/mysql/db.MYI
mysql/mysql/db.frm

... 중략

mysql/mysql/user.MYI
mysql/mysql/user.frm
mysql/test/
mysql/test_db/
mysql/test_db/db.opt
mysql/test_db/test_table.MYD
mysql/test_db/test_table.MYI
mysql/test_db/test_table.frm
mysql/test_db/test_table2.MYD
mysql/test_db/test_table2.MYI
mysql/test_db/test_table2.frm

sent 3058982 bytes  received 1532 bytes  244841.12 bytes/sec
total size is 27631451  speedup is 9.03

real 0m12.251s
user 0m0.801s
sys 0m0.446s
[root@CentOS6 ~]#
[root@CentOS6 ~]#

 

 

6. 실시간 Transection 종료 및 인입된 Row Count 확인

 

[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]# fg
while [ 0 ]; do
    mysql -u helperchoi -p1234567 -D test_db -e "INSERT INTO test_table2 (data) VALUES ('`echo $RANDOM | md5sum`');";
done (wd: /)

Ctrl+C Aborted

[root@CentOS6 ~]# fg
while [ 0 ]; do
    mysql -u helperchoi -p1234567 -D test_db -e "INSERT INTO test_table (data) VALUES ('`echo $RANDOM | md5sum`');";
done (wd: /)

Ctrl+C Aborted

[root@CentOS6 ~]# 

[root@CentOS6 ~]# 

[root@CentOS6 ~]# /root/shell/mysql_table_count.sh
DATABASE SIZE (test_db) : 7448 KB

TABLE COUNT1 (test_table) :
+----------+
| COUNT(*) |
+----------+
|   102403 |
+----------+

TABLE COUNT2 (test_table2) :
+----------+
| COUNT(*) |
+----------+
|    52337 |
+----------+

[root@CentOS6 ~]#

 

 

7. MySQL DB 서비스 중단 및 변경분 Data Migration

 

[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]# service mysqld stop
Stopping mysqld:                                           [  OK  ]
[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]# time rsync -avtz /NAS1/mysql /NAS2/
sending incremental file list
mysql/
mysql/ib_logfile0
mysql/ibdata1
mysql/test_db/test_table.MYD
mysql/test_db/test_table.MYI
mysql/test_db/test_table2.MYD
mysql/test_db/test_table2.MYI

sent 3778064 bytes  received 133 bytes  444493.76 bytes/sec
total size is 29454895  speedup is 7.80

real 0m7.572s
user 0m0.816s
sys 0m0.244s
[root@CentOS6 ~]#
[root@CentOS6 ~]#

 

 

8. MySQL DB Data 영역 교체 후 서비스 기동 및 로그 확인

 

[root@CentOS6 ~]#
[root@CentOS6 ~]# vi /etc/my.cnf
[mysqld]
#datadir=/NAS1/mysql
#socket=/NAS1/mysql/mysql.sock
datadir=/NAS2/mysql
socket=/NAS2/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]# ls -l /var/lib/mysql
total 0
lrwxrwxrwx. 1 root root 22 May 11 13:20 mysql.sock -> /NAS1/mysql/mysql.sock
[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]# ln -sf /NAS2/mysql/mysql.sock /var/lib/mysql/mysql.sock
[root@CentOS6 ~]#
[root@CentOS6 ~]# ls -l /var/lib/mysql
total 0
lrwxrwxrwx 1 root root 22 Aug 11 02:22 mysql.sock -> /NAS2/mysql/mysql.sock
[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]# service mysqld start &&  tail -10 /var/log/mysqld.log
Starting mysqld:                                           [  OK  ]

 

150811  2:18:51 [Note] /usr/libexec/mysqld: Shutdown complete

150811 02:18:51 mysqld_safe mysqld from pid file /var/run/mysqld/mysqld.pid ended
150811 02:23:30 mysqld_safe Starting mysqld daemon with databases from /NAS2/mysql
150811  2:23:30  InnoDB: Initializing buffer pool, size = 8.0M
150811  2:23:30  InnoDB: Completed initialization of buffer pool
150811  2:23:31  InnoDB: Started; log sequence number 0 44233
150811  2:23:32 [Note] Event Scheduler: Loaded 0 events
150811  2:23:32 [Note] /usr/libexec/mysqld: ready for connections.
Version: '5.1.73'  socket: '/NAS2/mysql/mysql.sock'  port: 3306  Source distribution
[root@CentOS6 ~]#
[root@CentOS6 ~]#

 

 

9. 변경분 Data 이전 확인을 위한 MySQL DB 조회

 

[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]# /root/shell/mysql_table_count.sh
DATABASE SIZE (test_db) : 7480 KB

TABLE COUNT1 (test_table) :
+----------+
| COUNT(*) |
+----------+
|   102403 |
+----------+

TABLE COUNT2 (test_table2) :
+----------+
| COUNT(*) |
+----------+
|    52337 |
+----------+

[root@CentOS6 ~]#
[root@CentOS6 ~]#
[root@CentOS6 ~]#

 

 

 

다. rsync 사용을 위한 환경설정

 

상기 나. 에서 활용한 rsync 명령은 아래와 같이 별도의 rsync package 설치와 환경설정을 필요로 한다.

 

1. yum 을 이용한 rsync package 설치

 

[root@CentOS6 /]#
[root@CentOS6 /]# yum -y install rsync*
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: ftp.neowiz.com
 * extras: ftp.neowiz.com
 * updates: ftp.neowiz.com
Setting up Install Process

 

... 중략


Installed size: 682 k
Downloading Packages:
rsync-3.0.6-12.el6.x86_64.rpm                                                                        | 335 kB     00:00    
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
  Installing : rsync-3.0.6-12.el6.x86_64                                                                                1/1
  Verifying  : rsync-3.0.6-12.el6.x86_64                                                                                1/1

Installed:
  rsync.x86_64 0:3.0.6-12.el6                                                                                              

Complete!

 

 

2. rsync 사용을 위한 허용 설정

 

[root@CentOS6 /]#
[root@CentOS6 /]# vi /etc/xinetd.d/rsync
# default: off
# description: The rsync server is a good addition to an ftp server, as it \
# allows crc checksumming etc.
service rsync
{
 disable = no ##### yes -> no 로 변경
 flags  = IPv6
 socket_type     = stream
 wait            = no
 user            = root
 server          = /usr/bin/rsync
 server_args     = --daemon
 log_on_failure  += USERID
}
[root@CentOS6 /]#

 

 

3. rsync 접근제어 환경 설정

 

[root@CentOS6 /]#
[root@CentOS6 /]# vi /etc/rsyncd.conf
uid = root
gid = root
use chroot = yes
read only = yes
host allow = 127.0.0.1
max connection = 3
timeout 10

[SOURCE]
path = /NAS1/mysql
[root@CentOS6 /]#

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형
댓글
댓글쓰기 폼