들어가며…

안녕하세요. 우아한형제들 서비스개발실 주문시스템개발팀의 라태웅입니다.

어느 개발 조직이건 새로운 사람이 들어오면 그 사람의 PC에 새롭게 개발 환경을 셋팅해야 합니다.

이 때, 저는 세 가지 경우를 경험했는데요.

  1. 문서에 정리된 개발 환경 설정 방법을 받았다. 따라했는데 안된다.
  2. 구두로 설명해주시는데 잘 모르겠다.
  3. 쉘 스크립트로 한 방에 셋팅이 된다.

1번의 문제는 문서가 노후화되고 사람이 이해하기 쉽게, 따라하기 쉽게 작성하기가 어렵다는 단점이 있습니다.

2번의 문제는 전달자가 헷갈리거나 오래되어 잊어버린 경우 굉장한 혼선이 온다는 것, 거기에 신규 입사자 입장에선 이 조직이 과연 이대로 괜찮은가 하는 생각이 들 수도 있겠죠.

하지만 3번처럼 쉘 스크립트 한 방으로 셋팅이 된다면?!

신규 입사자의 감탄사가 들리시나요!? (와아아아아아아아아아!!!!!!!!!!)

Can you listen?

들리..십니까? 제 목!쏘리가! 하!늘에!

이 글에서 해볼 것

이 글에서는 두 가지 쉘 스크립트를 작성할건데요.

첫 번째,

  1. PATH에 Working Directory 추가하기.
  2. AWS CLI, AWS ElasticBeanstalk CLI 설치하기.
  3. AWS Credential 추가하기.

보너스로 PHP Codeigniter를 AWS ElasticBeanstalk에 Deploy 해보기.

두 번째,

  1. 로컬 환경에서 Docker에 웹서버(PHP Codeigniter) 올려보기.

이 말인즉슨, 신규 입사자는 첫 번째 쉘 스크립트로 기본적인 AWS 기반의 개발 환경이 갖춰지고, 두 번째 쉘 스크립트로 로컬 환경에서 개발이 바로 가능해진다는 것이죠!

하지만 이 글에서는 쉘 스크립트의 문법에 대해서는 다루지 않습니다! 이곳을 참고해주세요! 이 글에서는 쉘 스크립트로 꽤 많은 것을 편하게 할 수 있다는 것을 알려드리는데에 초점이 맞춰져 있습니다.

첫 번째 쉘 스크립트

먼저 프로젝트 폴더를 생성하고, 내부에 디렉토리 구조를 만들어봅니다.

$ cd ~
$ mkdir sspj
$ cd sspj
$ mkdir utilities
$ cd utilities
$ mkdir bin
$ mkdir tmp
$ cd ..
$ mkdir webserver

만들어진 폴더 구조는 아래와 같습니다.

-- sspj
  -- utilities
    -- bin
    -- tmp
  -- webserver

이제 쉘 스크립트를 작성해봅니다.

$ vi ~/sspj/utilities/bin/sspj_setup.sh

#! /bin/bash
# 윗 줄은 이 프로그램은 bash를 기반으로 실행된다는 뜻입니다.

# 실행된 쉘 스크립트의 절대 경로를 가져옵니다.
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do
  TARGET="$(readlink "$SOURCE")"
  if [[ $SOURCE == /* ]]; then
    SOURCE="$TARGET"
  else
    DIR="$( dirname "$SOURCE" )"
    SOURCE="$DIR/$TARGET"
  fi
done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"

BASEDIR=$DIR

IFS="/"
BASEDIR_SPLIT=($BASEDIR)
IFS=""

# Root 디렉토리를 구합니다.
SSPJ_HOME=""

for key in "${!BASEDIR_SPLIT[@]}";
	do {
		if [[ $key > 0 && $key < $(( ${#BASEDIR_SPLIT[@]} - 2 )) ]]; then
			SSPJ_HOME="$SSPJ_HOME""/""${BASEDIR_SPLIT[$key]}"
		fi;
	};
done

# bash_profile에 PATH를 추가해줍니다.
echo "export SSPJ_HOME=$SSPJ_HOME" >> ~/.bash_profile
echo "export PATH=$PATH:$SSPJ_HOME" >> ~/.bash_profile

echo "Root directory is $SSPJ_HOME"

# AWS CLI를 설치하기 위해 pip를 먼저 설치합니다.
echo "Installing pip ..."
sudo curl https://bootstrap.pypa.io/get-pip.py -o $SSPJ_HOME/utilities/tmp/get-pip.py
echo "Running get-pip.py ..."
sudo python $SSPJ_HOME/utilities/tmp/get-pip.py

# AWS CLI를 설치합니다.
echo "Instailling awscli ..."
sudo pip install --ignore-installed awscli

# AWS ElasticBeanstalk CLI를 설치합니다.
echo "Instailling awsebcli ..."
sudo pip install --ignore-installed awsebcli

# AWS Credential을 만듭니다.
echo "Input AWS Access Key : "
read AWS_ACCESS_KEY
echo "Input AWS Secret Access Key : "
read AWS_SECRET_ACCESS_KEY

sudo mkdir ~/.aws
sudo chmod -R 777 ~/.aws
sudo touch ~/.aws/credentials
sudo cat > ~/.aws/credentials << EOF
[default]
aws_access_key=$AWS_ACCESS_KEY
aws_secret_access_key=$AWS_SECRET_ACCESS_KEY
EOF
sudo touch ~/.aws/config
sudo cat > ~/.aws/config << EOF
[profile eb-cli]
aws_access_key_id=$AWS_ACCESS_KEY
aws_secret_access_key=$AWS_SECRET_ACCESS_KEY
EOF

# PATH를 터미널에 적용하기 위해 source를 해줍니다.
source ~/.bash_profile

# 끝!
echo "Done!"

이제 이 쉘 스크립트를 실행 가능한 상태로 만들어줍니다.

$ chmod +x ~/sspj/utilities/bin/sspj_setup.sh

chmod

ls -al 명령어로 확인하면 실행 권한이 추가된 것을 확인할 수 있습니다.

이제 만든 쉘 스크립트를 실행해 볼까요?

$ source ~/sspj/utilities/bin/sspj_setup.sh

script_01_01

패스워드를 입력하면..

script_01_02

이것저것 설치를 합니다!

script_01_03

AWS Access Key를 넣어주면..

script_01_04

이렇게 자동으로 Credential 까지 생기게 되죠!

잠깐! 혹시 AWS Access Key가 없으신가요?

iam

AWS에 로그인 후 IAM 서비스로 이동합니다.

iam2

Groups > Create New Group을 눌러 새로운 그룹을 생성합니다.

iam3

Group Name을 적은 후 Next Step을 누릅니다.

iam4

테스트용으로 쓸거기 때문에 AdministratorAccess를 체크하고 Next Step을 누른 다음, Create Group을 눌러 그룹 생성을 마칩니다.

iam5

이제 유저를 생성할 차례입니다. Add User를 눌러주세요.

iam6

사진과 같이 적은 후 Next: Permissions를 누릅니다.

iam7

방금 만들었던 SSPJ 그룹에 유저를 추가하고 Next: Review를 누른 뒤, Create User를 눌러 유저 생성을 마칩니다.

iam8

SYSTEM : 독자이(가) Access Key을(를) 획득했다!

이제 다시 돌아와서…

AWS ElasticBeanstalk에 CLI로 배포하기

먼저, webserver.zip 파일을 다운로드 받고, ~/sspj/webserver 폴더에 압축을 풉니다.

AWS ElasticBeanstalk에 관한 설명은 Elastic Beanstalk Configuration files(.ebextensions)에서도 보실 수 있습니다. 나는 쉘 스크립트만 궁금하다 하시는 분은 이 파트를 건너뛰셔도 됩니다!

PHP Codeigniter 기반으로 작성된 뼈대 웹서버 인데요. 이를 AWS ElasticBeanstalk CLI로 간편하게 배포해보겠습니다.

$ cd ~/sspj/webserver
$ eb init

위의 eb는 AWS ElasticBeanstalk CLI 인데요. 아까 첫 번째 쉘 스크립트가 자동으로 설치해준 녀석입니다. init 명령어를 통해 최초 설정을 해줍니다.

eb_init

서울을 선택해줍니다.

eb_init2

새로운 어플리케이션을 만듭니다.

eb_init3

어플리케이션 이름은 sspj로 해줍니다.

eb_init4

네. 우리의 웹서버는 PHP가 맞습니다.

eb_init5

저는 항상 최신 버전으로 개발하려고 노력합니다.

eb_init6

이 글에선 안 쓰지만 아쉬우니 SSH Key도 만들어 봅니다.

eb_init7

새로운 키페어를 생성합니다.

eb_init8

키페어 이름은 sspj-keypair로 하겠습니다.

eb_init9

패스워드는 자유롭게 설정합니다.

ElasticBeanstalk Application이 만들어졌습니다. 이제 Environment를 만듭니다.

$ eb create

eb_create

Environment 이름과 도메인 Prefix를 설정합니다.

eb_create2

로드밸런서는 클래식으로 해봅니다.

eb_create3

로그가 찍히더니 성공했다고 나옵니다!

eb_create4

웹 콘솔로 접근해보니 잘 배포되었군요!

eb_create5

접속도 잘 되구요!

이제 다음 배포부터는 정말 간단합니다.

$ eb deploy

정말 쉽죠?

그런데 개발할 때 테스트를 위해서 기능을 수정할 때마다 deploy를 하고 수정하고 하는 식은 너무 불편하겠죠. 그래서 이를 Docker로 로컬 환경에서 실행해보겠습니다.

두 번째 쉘 스크립트

두 번째 쉘 스크립트에 작성하기 전에, webserver.zip 파일을 다운로드 받고, ~/sspj/webserver 폴더에 압축을 풉니다. 이미 앞선 파트에서 받으셨다면 받지않으셔도 됩니다.

저희는 Docker를 통해 개발 환경을 설정할 것이기 때문에 먼저 Docker를 설치해야 합니다. (이곳(Docker for Mac)에서 설치할 수 있습니다.)

먼저 Dockerfile을 만듭니다.

Dockerfile에 대한 자세한 설명은 진행하지 않습니다! 이런식으로 쉘 스크립트를 사용할 수 있다고만 봐주세요!

$ vi ~/sspj/Dockerfile_ws
FROM php:7.0.12-apache

ENV DEBIAN_FRONTEND=noninteractive

# Install the PHP extensions I need for my personal project (gd, mbstring, opcache)
RUN apt-get update && apt-get install -y libpng12-dev libjpeg-dev libpq-dev git mysql-client-5.5 wget \
	&& rm -rf /var/lib/apt/lists/* \
	&& docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \
	&& docker-php-ext-install gd mbstring opcache pdo zip

RUN apt-get update && apt-get install -y apt-utils adduser curl nano debconf-utils bzip2 dialog locales-all zlib1g-dev libicu-dev g++ gcc locales make build-essential

# Install mysql extension
RUN apt-get update && apt-get install -y --force-yes \
    freetds-dev \
 && rm -r /var/lib/apt/lists/* \
 && cp -s /usr/lib/x86_64-linux-gnu/libsybdb.so /usr/lib/ \
 && docker-php-ext-configure pdo_mysql --with-pdo-mysql=mysqlnd \
 && docker-php-ext-install \
    pdo_dblib \
    pdo_pgsql

# APC
RUN pear config-set php_ini /usr/local/etc/php/php.ini
RUN pecl config-set php_ini /usr/local/etc/php/php.ini
RUN pecl install apc

RUN a2enmod rewrite
RUN a2enmod expires
RUN a2enmod mime
RUN a2enmod filter
RUN a2enmod deflate
RUN a2enmod proxy_http
RUN a2enmod headers
RUN a2enmod php7

# Edit PHP INI
RUN echo "memory_limit = 1G" > /usr/local/etc/php/php.ini
RUN echo "upload_max_filesize = 1000M" >> /usr/local/etc/php/php.ini
RUN echo "post_max_size = 1000M" >> /usr/local/etc/php/php.ini
RUN echo "max_input_vars = 10000" >> /usr/local/etc/php/php.ini
RUN echo "max_input_time = 60" >> /usr/local/etc/php/php.ini
RUN echo "file_uploads = On" >> /usr/local/etc/php/php.ini
RUN echo "max_execution_time = 300" >> /usr/local/etc/php/php.ini
RUN echo "LimitRequestBody = 100000000" >> /usr/local/etc/php/php.ini

# Clean after install
RUN apt-get autoremove -y && apt-get clean all

# Configuration for Apache
RUN rm -rf /etc/apache2/sites-enabled/000-default.conf
ADD utilities/bin/ws-config/000-default.conf /etc/apache2/sites-available/
RUN ln -s /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-enabled/
RUN a2enmod rewrite

EXPOSE 80

# Change website folder rights and upload your website
RUN chown -R www-data:www-data /var/www

# Change working directory
WORKDIR /var/www/webserver

# Change your local - here it's in french
RUN echo "locales locales/default_environment_locale select ko_KR.UTF-8" | debconf-set-selections \
&& echo "locales locales/locales_to_be_generated multiselect 'ko_KR.UTF-8 UTF-8'" | debconf-set-selections
RUN echo "Asia/Seoul" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdata

# Change folder permission
RUN chmod -R 0777 /var/www/webserver

# RUN service apache2 restart

CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

Docker에서 바라볼 config 파일을 생성합니다.

$ cd ~/sspj/utilities/bin
$ mkdir ws-config
$ cd ws-config
$ vi 000-default.conf
<VirtualHost *:80>
        ServerAdmin webmaster@localhost
        ServerName localhost

        DocumentRoot /var/www/webserver
        <Directory />
                Options FollowSymLinks
                AllowOverride All
        </Directory>
        <Directory /var/www/webserver >
                Options Indexes FollowSymLinks MultiViews
                AllowOverride All
                Order allow,deny
                allow from all
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/error.log

        # Possible values include: debug, info, notice, warn, error, crit,
        # alert, emerg.
        LogLevel warn

        CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

이제 이 Docker를 자동으로 빌드하고 실행하는 스크립트를 작성합니다.

$ vi ~/sspj/utilities/bin/run_ws_local.sh
#! /bin/bash
# 윗 줄은 이 프로그램은 bash를 기반으로 실행된다는 뜻입니다.

# 이미 실행중인 컨테이너가 있다면 멈추고
docker stop sspj-ws-container
# 제거합니다.
docker rm sspj-ws-container
# Dockerfile을 빌드하고
docker build -f $SSPJ_HOME/Dockerfile_ws . -t sspj-ws
# 실행합니다.
docker run -it -d -p 80:80 --name sspj-ws-container -v $SSPJ_HOME/webserver:/var/www/webserver sspj-ws

쉘 스크립트 자체는 정말 짧지요?

실행해보겠습니다. (일단 실행 권한을 먼저 줘야겠죠?)

$ chmod +x ~/sspj/utilities/bin/run_ws_local.sh
$ ~/sspj/utilities/bin/run_ws_local.sh

docker

뭔가 열심히 설치를

docker1

다 했더니!

docker2

로컬에서 바로 접속이 됩니다!

마치며…

이번 글에서는 쉘 스크립트를 한 번 만들어두면 개발 환경을 쉽게 구성할 수 있습니다! 라는 것을 전달드리고 싶었는데요… 다 적고보니 뭔가 뒤죽박죽이 된 것 같습니다 -0-…

결론은! 이렇게 누군가가 셋업을 해두면, 다음 사람들은 Docker를 설치하기만 하면

$ source ~/sspj/utilities/bin/quest_setup.sh
$ . ~/sspj/utilities/bin/run_ws_local.sh

이 명령어로 바로 개발이 가능하다는 것이죠.

여러분의 개발 환경에도 적용해보시는 것은 어떨까요?

감사합니다!