๋ฌด์ค๋จ ๋ฐฐํฌ ์ ๋ต์๋ ์ฌ๋ฟ ์๋ค. ์ฐ์
-
Rolling
- ์ผ๋ฐ์ ์ธ ๋ฐฐํฌ๋ฅผ ์๋ฏธํ๊ณ , ๋จ์ํ๊ฒ ์๋ฒ๋ฅผ ๊ตฌ์ฑํด ๋ฐฐํฌํ๋ ์ ๋ต์ด๋ค. ๋ค์ ๋งํด ๊ตฌ ๋ฒ์ ์์ ์ ๋ฒ์ ์ผ๋ก ํธ๋ํฝ์ ์ ์ง์ ์ผ๋ก ์ ํํ๋ ๋ฐฐํฌ์ด๋ค. ๊ด๋ฆฌ๊ฐ ํธํ์ง๋ง, ๋ฐฐํฌ ์ค ํ์ชฝ ์ธ์คํด์ค์ ์๊ฐ ๊ฐ์๋๋ฏ๋ก ์๋ฒ ์ฒ๋ฆฌ ์ฉ๋์ ๋ฏธ๋ฆฌ ๊ณ ๋ คํ๋ค.
- ์ถ๊ฐ์ ์ธ ์๋ฒ๊ฐ ํ์ํ์ง ์๋ค.
- ์ด์ ๋ฒ์ ๊ณผ ์๋ก์ด๋ฒ์ ์ ํ๊ฒฝ์ด ๊ณ ๋ฆฝ๋์ง ์๊ธฐ ๋๋ฌธ์ Blue-green๋ณด๋ค ๋น ๋ฅด์ง๋ง ๋จ์ ์ ๊ณ ๋ฆฝ๋์ง ์์ ๋กค๋ฐฑ์ด ์ด๋ ต๋ค๋ ๊ฒ์ด๋ค. ๋ฐฐํฌํ ์๋ฒ๊ฐ ์๋ชป๋๋ค๋ฉด ๋กค๋ง๋ฐฉ์์ ๋๊ฐ์ด ์ด์ฉํด ๋กค๋ฐฑํด์ผ ํ๋ค.
-
in-place
- ์ธ์คํด์ค๊ฐ์ ์ธํ๋ผ ์ปดํฌ๋ํธ์ ๊ต์ฒด ์์ด ๋ฒ์ ์ ์ ๋ฐ์ดํธํ๋ ๋ฐฉ์์ด๋ค.
- ํ์ฌ ๊ฐ๋์ค์ธ ์๋ฒ ๊ทธ๋๋ก ๋ฌด์ค๋จ ๋ฐฐํฌ๋ฅผ ๊ตฌํํ๊ธฐ ๋๋ฌธ์ ํ์ฌ์์น ๋ฐฐํฌ๋ผ๊ณ ๋ถ๋ฆฐ๋ค.
-
blue-green
- ๋ถ๋ฆฌ๋์ง๋ง ๋์ผํ ํ๊ฒฝ์ ๋ง๋ค์ด์ผ ํ๋ค.
- ๊ตฌ ๋ฒ์ ์ ๋ธ๋ฃจ, ์ ๋ฒ์ ์ ๊ทธ๋ฆฐ ์ด๋ผ๊ณ ํด์ ๋ถ์ฌ์ง ์ด๋ฆ์ด๋ค. ์ ๋ฒ์ ์ ๋ฐฐํฌํ๊ณ ์ผ์ ํ ์ ํํ์ฌ ๋ชจ๋ ์ฐ๊ฒฐ์ ์ ๋ฒ์ ์ ๋ฐ๋ผ๋ณด๊ฒ ํ๋ ์ ๋ ฅ์ด๋ค. ๊ตฌ ๋ฒ์ , ์ ๋ฒ์ ์๋ฒ๋ฅผ ๋์์ ๋๋ํ ๊ตฌ์ฑํ์ฌ ๋ฐฐํฌ์์ ์ ํธ๋ํฝ์ด ์ผ์ ํ ์ ํ๋๋ค. ๋น ๋ฅธ ๋กค๋ฐฑ์ด ๊ฐ๋ฅํ๊ณ , ์ด์ํ๊ฒฝ์ ์ํฅ์ ์ฃผ์ง ์๊ณ ์ค์ ์๋น์ค ํ๊ฒฝ์ผ๋ก ์ ๋ฒ์ ํ ์คํธ๊ฐ ๊ฐ๋ฅํ๋ค. ๋จ ์ด๋ฐ ๊ตฌ์ฑ์ ์์คํ ์์์ด ๋๋ฐฐ๋ก ํ์ํ์ฌ ๋น์ฉ์ด ๋ ๋ง์ด ๋ฐ์
- ์ฅ์ ์ ๋ฐฐํฌ์๋๊ฐ ๋น ๋ฅด๋ฉฐ, ์ฅ์ ๊ฐ ๋ฐ์ํ์๋ ๋ก๋ ๋ฐธ๋ฐ์๊ฐ ๊ธฐ์กด์๋ฒ๋ฅผ ๊ฐ๋ฆฌํค๊ธฐ๋ง ํ๋ฉด ๋๊ธฐ ๋๋ฌธ์ ๋กค๋ฐฑ์ด ์ฝ๋ค.
- ์ ๋ฐ์ดํธ๋ฅผ ์งํํ๋ ๋์ค์์ ์๋ฒ์๋ ๊ทธ๋๋ก ์ ์ง๋๊ธฐ ๋๋ฌธ์ ๊ธฐ์กด ์ฌ์ฉ๋๋ ๊ทธ๋๋ก ์๋ฒ๋ฅผ ์ ๋ฐ์ดํธ ํ๋ฉด๋๋ค. ๊ทธ๋์ ๋ถํ๊ฐ ๊ฑธ๋ฆด ๋ฌธ์ ๋ฅผ ์๊ฐํ์ง ์์๋ ๋๋ค.
- ๋ง์ฝ ์๋ฒ๊ฐ ๊ฐ์ํ๊ฒฝ์ด ์๋๊ณ ๋ฌผ๋ฆฌ์ ์ธ ์๋ฒ๋ก ์กด์ฌํ๋ค๋ฉด ์๋ฌด๋ฆฌ ๋์ด ๋ง์ ํ์ฌ๋ ์๋ฒ๋ฅผ ๋๋ฐฐ๋ก ๋๋ ธ๋ค๊ฐ ํ์์์ด์ง๋ฉด ์ค์ด๋ ๋นํจ๋ฅ ์ ์ ํํ ์ ์๋ค. ๋ฐ๋ผ์ ๋ฌผ๋ฆฌ์ ์ผ๋ก ์กด์ฌํ๋ ์๋ฒ์์๋ ์ฌ์ฉํ๊ธฐ ์ด๋ ค์ฐ๋ฉฐ ํ์ฌ์์น ๋ฐฐํฌ ๋ฐฉ์์ด ๋ ์ด์ธ๋ฆฐ๋ค. ๋ ์ธ์คํด์ค๋ฅผ ์ฝ๊ฒ ์์ฑํ๊ณ ์ง์ธ ์ ์๋ ํด๋ผ์ฐ๋ ํ๊ฒฝ์ด๋. ์ปจํ ์ด๋๋ฅผ ์ฌ๋ ธ๋ค๊ฐ ๋ด๋ฆฌ๋ ๊ฒ์ด ์์ ๋ก์ด Docker๋ฑ ๊ฐ์ํ๊ฒฝ์ ์ด์ธ๋ฆฐ๋ค.
-
carney
- ์นด๋๋ฆฌ๋ ์๋ฅผ ์๋ฏธํ๋๋ฐ ์ ๋ ๊ฐ์ค์ ๊ต์ฅํ ๋ฏผ๊ฐํ ๋๋ฌผ๋ก ๊ด์ฐ์์ ์ํ์ ์๋ฆฌ๋ ์ฉ๋๋ก ์ฌ์ฉ๋์ด์๋ค. ์ฆ, ๋ฏธ๋ฆฌ ์ํ์ ๊ฐ์งํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ๋ฐฐํฌ ๋ฐฉ์์ด๋ค.
- ์นด๋๋ฆฌ ๋ฐฐํฌ๋ ์ํ์ ๋น ๋ฅด๊ฒ ๊ฐ์งํ ์ ์๋ ๋ฐฐํฌ ์ ๋ต์ด๋ค. ์ง์ ํ ์๋ฒ ๋๋ ํน์ user์๊ฒ๋ง ๋ฐฐํฌํ๋ค๊ฐ ์ ์์ ์ด๋ฉด ์ ์ฒด๋ฅผ ๋ฐฐํฌํ๋ค. ์๋ฒ์ ํธ๋ํฝ์ ์ผ๋ถ๋ฅผ ์ ๋ฒ์ ์ผ๋ก ๋ถ์ฐํ์ฌ ์ค๋ฅ ์ฌ๋ถ๋ฅผ ํ์ธํ ์ ์๋ค. ์ด๋ฐ ์ ๋ต์ A/B ํ ์คํธ๊ฐ ๊ฐ๋ฅํ๋ฉฐ, ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง์ ์ ์ฉํ๋ค. ํธ๋ํฝ์ ๋ถ์์ํฌ ๋๋ ๋ผ์ฐํ ์ ๋๋คํ๊ฒ ํ ์ ์๊ณ ์ฌ์ฉ์๋ก ๋ถ๋ฅ ํ ์๋ ์๋ค.
- ๊ฐ์ฅ ํฅ๋ฏธ๊ฐ ์๊ฒผ๋ ๋ฐฐํฌ๋ฐฉ์์ด๋ค.
- ์กฐ๊ธ์ฉ ์ฌ์ฉ์์ ๋ฒ์๋ฅผ ๋๋ ค๊ฐ๋ฉฐ ํผ๋๋ฐฑ์ ํตํด ๋ฐฐํฌํ๋ ๋ฐฉ์(์ค์ ์ธ์คํ๊ทธ๋จ์์ ์น๊ตฌ๋ค๊ณผ ๋ค๋ฅธ๋ฒ์ ์ ์ธ์คํ๋ฅผ ๋ชฉ๊ฒฉํ์ ์ด ์๋ค.)
- ๋จ๊ณ๋ณ ๋ฐฐํฌ๋ฅผ ์๋ฏธํ๋ค.
- ์ฅ์ ๋ง ์กด์ฌํ๋ ๊ฒ์ด ์๋๋ค. ๋ํ์ ์ธ ์ฅ์ ์ด ๊ด๋ฆฌ๋น์ฉ์ด ๋๋ฌด ํฌ๋ค๋ ๊ฒ.
- ๋ฒ์ ๊ด๋ฆฌ๋ฅผ ์ํด ๋์์ ์ฌ๋ฌ๊ฐ์ ์ํํธ์จ์ด ๋ฒ์ ์ ๊ด๋ฆฌํด์ผ ํ๋ ๊ฒ์ด๋ค.
- ๋ก๋๋ฒจ๋ฐ์๊ฐ ํ์ํ๋ค. ec2 nginx๋ก ํ๋ด๋ฅผ ๋ผ์ ์์ ๊ฒ ๊ฐ์ง๋ง ๊ต์ฅํ ๋ญ๋น๋ผ๊ณ ์๊ฐ
- A/B Testing ๊ณผ์ ์ฐจ์ด
- ๊ธฐ์ ์ ๊ตฌํ์ ์ ์ฌ์ฑ์ผ๋ก A/B Testing์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ด ๋ ์ ์์ง๋ง ๋์ ๋ค๋ฅธ ๊ฐ๋
- ์นด๋๋ฆฌ ๋ฐฐํฌ๋ ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ์ด์์ด ์๋ ๊ฒฝ์ฐ ๋กค๋ฐฑํ๋ ๊ฒ์ ์ด์ ์ด ๋ง์ถฐ์ ธ ์์ง๋ง
- A/B Testing ์ ํ๋์ ๊ฐ์ค์ ๋ค์ํ ๊ตฌํ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ํ ์คํธํ๋๊ฒ์ ๋ชฉ์ ์ผ๋ก ํ๊ธฐ ๋๋ฌธ์ด๋ค.
-
์ด๋ฐ ๋ฐฉ์์ ์ฌ์ฉํ ์ด์
๊ฐ๊ฐ๋ฐฉ์์๋ ์ฅ๋จ์ ์ด ์๋ค.
์ฐ์ Rolling ์ ์ถ๊ฐ์ ์ธ ์๋ฒ๊ฐ ํ์ํ์ง ์๋ ์ฅ์ ์ด ์์ง๋ง ๋ค๋ฅธ ๋ฒ์ ์ ์๋ฒ๋ค์ด ๊ณ ๋ฆฝ๋์ง ์์ ๋กค๋ฐฑ์ด ์ด๋ ต๋ค๋ ๊ฒ์ด๋ค. ๋ฐฐํฌํ ์๋ฒ๊ฐ ์๋ชป๋๋ค๋ฉด ๋ฐฐํฌโ ๋กค๋ฐฑ โ ๋ฐฐํฌ ๋กค๋ง ๋ฐฉ์์ ๋๊ฐ์ด ์ด์ฉํด ๋กค๋ฐฑํด์ผ ํ๋ค.
๋ ์๋ฒ๋ฅผ ๋๋ ์ ์ ๋ฐ์ดํธํ๊ธฐ ๋๋ฌธ์ ์ค๋จ๋ ์๋ฒ์ ์์ฒญ์ ๋๋จธ์ง ์๋ฒ๊ฐ ๊ฐ๋นํด์ผํ๋ค. ๊ฐ์๊ธฐ ์ฌ์ฉ์๊ฐ ๋ชฐ๋ฆฌ๋ฉด ๋ฌธ์ ๊ฐ ์๊ธธ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๋ค.
์ฐ๋ฆฌ๊ฐ ์๊ณ ์๋ Blue-Green์ ๋ฆฌ์์ค์ ๋ญ๋น๊ฐ ํด ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๋ค. EC2 ์ธ์คํด์ค๋ฅผ ์ฌ๋ฌ๊ฐ ๋๋ฆฐ๋ค๋ ๊ฒ์ ์ฐ๋ฆฌ๊ฐ์ ํ์์ ์ฅ์์ ๋๋ฌด ํฐ ๋ญ๋น์ด๋ค. ๋ ๋ก๋๋ฒจ๋ฐ์๊ฐ ํ์ํ๋ฐ ๋ก๋๋ฒจ๋ฐ์์ญํ ์ ํ Nginx๋ฅผ ์ํ ์ธ์คํด์ค๋ฅผ ๋ง๋๋๊ฒ ๋ํ ๋ญ๋น๋ผ๊ณ ์๊ฐํ๋ค.
์นด๋๋ฆฌ ๋ฐฐํฌ ๋ํ ๋ก๋๋ฒจ๋ฐ์๊ฐ ํ์ํ๊ณ ๋ง์ฝ์ ๋ก๋๋ฒจ๋ฐ์์ ๋ชจ๋ ํ๊ฒฝ์ ๊ฐ์ถ๊ณ ๋ ๋ค ํด๋น ๋ฐฐํฌ์ ๋ต์ ์ฌ์ฉํ๋ค๊ณ ํด๋ ๊ตฌ ๋ฒ์ ์ ์๋ฒ์ ์ ๋ฒ์ ์ ์๋ฒ๋ฅผ ๊ฐ์ด ๊ด๋ฆฌํด์ผํ๋ ๊ฒ ๋ํ ๋ฌธ์ ๋ผ๊ณ ์๊ฐํ๋ค. ํ์์ด 5๋ช ์ด๋ค.
ํ์ฌ ์ฐ๋ฆฌ์๋ฒ๋ EC2์ docker ๋ฅผ ์ฌ์ฉํด Nginx ์ application ์๋ฒ๋ฅผ ๋๋ฆฌ๊ณ ์๋ค.
์ฆ ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ ์ง์ฐ๋๋ฐ ํธํ๊ณ htop๋ฅผ ์ด์ฉํด ๋ณธ๊ฒฐ๊ณผ ํ์ฌ 3๊ธฐ๊ฐ์ ์ปดํจํฐ ์์์ค 700m๋ฐ์ ์ฌ์ฉํ์ง ์์๋ค. ํผ์ผํธ๋ก ๊ณ์ฐํด๋ณด๋ฉด 20ํผ์ํธ๋ ์๋๋ ๊ฐ์ด๋ค.
์์ ์ ์ธ ์๋น์ค๋ฅผ ์ํ CPU์ฌ์ฉ์จ์ด ์ต๋ 70ํผ์ผํธ์ธ ์์น์ ๋น๊ต๋ฅผ ํ์๋ ํฌ๊ฒ ๋ถ์กฑํ ์์น์ด๋ค. ๋ฐ๋ผ์
๋์ปค๋ฅผ ์ด์ฉํด ํ๋์ ์ธ์คํด์ค ๋ด์์ ์ฌ๋ฌ ์๋ฒ ๊ทธ๋ฃน์ ๋ง๋ค์ด Blue-green ๋ฐฉ์์ ์ปจ์ ์ ์ฌ์ฉํด ๋ฌด์ค๋จ๋ฐฐํฌ๋ฅผ ํ๊ธฐ๋ก ํ๋ค.
#!/bin/bash
CURRENT_PORT=$(docker ps | grep springboot | awk {'print $11'}| awk -F '/' {'print $1'} | awk -F '->' {'print $2'})
CURRENT_IP=$(cat /etc/nginx/sites-available/service-url.inc | grep -o http://172.* | sed 's/;$//')
echo $CURRENT_PORT
SERVER1=8080
SERVER2=8081
if [ $CURRENT_PORT -eq $SERVER1 ]
then
echo " > ํ์ฌ SERVER_PORT: $CURRENT_PORT , SERVER2 CONTAINER ์คํ"
IDLE_PROFILE=$SERVER2
IDLE_PORT=8081
sudo docker build --force-rm -t springboot .
sudo docker rmi $(docker images -f "dangling=true" -q)
sudo docker stop springboot2
sudo docker rm $(sudo docker ps -a -f "name=springboot2" -q)
sudo docker run -d -p 8081:8081 --volume /home/ubuntu/seller-lee/log:/back/build/libs/log --name springboot2 springboot
CONTAINER_ONE_IP=`sudo docker inspect -f "{{ .NetworkSettings.IPAddress }}" springboot1`
CONTAINER_TWO_IP=`sudo docker inspect -f "{{ .NetworkSettings.IPAddress }}" springboot2`
if [ "$(docker ps | grep springboot2)" ]
then
for retry_count in {1..10}
do
response=$(curl -s https://${CONTAINER_TWO_IP}:${SERVER2}/health)
count=$( echo $response | grep Download | wc -l)
if [ $count -ge 1 ]
then
echo " > health check ์ฑ๊ณต"
break
else
echo "> Health check์ ์๋ต์ ์ ์ ์๊ฑฐ๋ ํน์ status๊ฐ UP์ด ์๋๋๋ค."
echo "> Health check: ${response}"
fi
if [ $retry_count -eq 10 ]
then
echo "> Health check ์คํจ. "
echo "> Nginx์ ์ฐ๊ฒฐํ์ง ์๊ณ ๋ฐฐํฌ๋ฅผ ์ข
๋ฃํฉ๋๋ค."
exit 1
fi
echo "> Health check ์ฐ๊ฒฐ ์คํจ. ์ฌ์๋..."
sleep 10
done
echo " > ๋ณ๊ฒฝํ SERVER_PORT: $SERVER1 , CONTAINER_IP: $CONTAINER_ONE_IP ์คํ ์๋ฃ"
echo " > ๋ณ๊ฒฝ SERVER_PORT: $SERVER2 , CONTAINER_IP: $CONTAINER_TWO_IP ์คํ ์๋ฃ"
sudo sed -i "s/$CONTAINER_ONE_IP:$SERVER1;/$CONTAINER_TWO_IP:$SERVER2;/g" /etc/nginx/sites-available/service-url.inc
docker exec nginx nginx -s reload
docker stop springboot1
echo " > CONTAINER_IP: $CONTAINER_ONE_IP ์ข
๋ฃ"
fi
elif [ $CURRENT_PORT -eq $SERVER2 ]
then
echo " > ํ์ฌ SERVER_PORT: $CURRENT_PORT , SERVER1 CONTAINER ์คํ"
IDLE_PROFILE=$SERVER1
IDLE_PORT=8080
sudo docker build --force-rm -t springboot .
sudo docker rmi $(docker images -f "dangling=true" -q)
sudo docker stop springboot1
sudo docker rm $(sudo docker ps -a -f "name=springboot1" -q)
sudo docker run -d -p 8080:8080 --volume /home/ubuntu/seller-lee/log:/back/build/libs/log --name springboot1 springboot
CONTAINER_ONE_IP=`sudo docker inspect -f "{{ .NetworkSettings.IPAddress }}" springboot1`
CONTAINER_TWO_IP=`sudo docker inspect -f "{{ .NetworkSettings.IPAddress }}" springboot2`
if [ "$(docker ps | grep springboot1)" ]
then
for retry_count in {1..10}
do
response=$(curl -s https://${CONTAINER_ONE_IP}:${SERVER1}/health)
count=$( echo $response | grep Download | wc -l)
if [ $count -ge 1 ]
then
echo " > health check ์ฑ๊ณต"
break
else
echo "> Health check์ ์๋ต์ ์ ์ ์๊ฑฐ๋ ํน์ status๊ฐ UP์ด ์๋๋๋ค."
echo "> Health check: ${response}"
fi
if [ $retry_count -eq 10 ]
then
echo "> Health check ์คํจ. "
echo "> Nginx์ ์ฐ๊ฒฐํ์ง ์๊ณ ๋ฐฐํฌ๋ฅผ ์ข
๋ฃํฉ๋๋ค."
exit 1
fi
echo "> Health check ์ฐ๊ฒฐ ์คํจ. ์ฌ์๋..."
sleep 10
done
sudo sed -i "s/$CONTAINER_TWO_IP:$SERVER2;/$CONTAINER_ONE_IP:$SERVER1;/g" /etc/nginx/sites-available/service-url.inc
echo " > ๋ณ๊ฒฝํ SERVER_PORT: $SERVER1 , CONTAINER_IP: $CONTAINER_ONE_IP ์คํ ์๋ฃ"
docker exec nginx nginx -s reload
docker stop springboot2
echo " > CONTAINER_IP: $CONTAINER_TWO_IP ์ข
๋ฃ"
fi
fi
-
์ฐธ๊ณ
- https://jojoldu.tistory.com/267
- https://reference-m1.tistory.com/211
- https://onlywis.tistory.com/10
- https://tech.devsisters.com/posts/blue-green-canary-deployment/
- https://medium.com/sjk5766/nginx-docker๋ฅผ-ํ์ฉํ-๋ฌด์ค๋จ-๋ฐฐํฌ๋ง๋ณด๊ธฐ-8b4f8571ab24
- https://gist.github.com/ninanung/9d63304cb0d070642e89f9b94b6fe24b
- https://chigon.tistory.com/entry/์ฑ๋ฅ-ํ ์คํธ์-์๋ฒ-๋ชจ๋ํฐ๋ง-๋ฐฉ๋ฒ-์ ๋ฆฌ
- https://blog.dramancompany.com/2017/04/aws-code-deploy๋ฅผ-ํตํ-๋ฐฐํฌ-์๋ํ/
- ๋์น์ ๊ฐ๋ฐ ์ด์ผ๊ธฐ
- aws
-
script ์ฐธ๊ณ
- #!bin/bash ์ ์๋ฏธ
- Shell script(์) if ์กฐ๊ฑด๋ฌธ, ์กฐ๊ฑด์
- (๋ฆฌ๋ ์ค / ์ ๋์ค / ์ ธ ์คํฌ๋ฆฝํธ) ์กฐ๊ฑด๋ฌธ๊ณผ ํจ๊ป ์ฐ์ด๋ test ๋ช ๋ น์ด ์ฌ์ฉ๋ฒ ๋ฐ ํ์ฉ ์์
- (๋ฆฌ๋ ์ค / ์ ๋์ค / ์ ธ ์คํฌ๋ฆฝํธ) IF๋ฌธ ์กฐ๊ฑด๋ฌธ - ์ ธ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ์ด
- [Linux] Shell ๋ช ๋ น์ด์์ &&์ ;์ ์ฐจ์ด
- Bash ์ ๋ฌธ์๋ฅผ ์ํ ํต์ฌ ์์ฝ ์ ๋ฆฌ (Shell Script)