AWS EC2에서 BitBucket으로 자동 배포 시스템(Auto Deployment) 구축하기

2018. 7. 17. 14:08개발을 파헤치다/서버 인프라

반응형

배포 프로세스


BitBucket 환경구축

ssh key 생성하기

cd ~/.ssh
ssh-keygen -t rsa


위와 같이 명령어를 입력하면 key 파일 이름을 입력하라고 나옵니다.
용도에 맞게 적당히 키이름을 입력하면 passphrase를 입력하라고 나오는데 이 부분은 넘어가게 됩니다.


엔터 그리고 다시 엔터를 누르면 위와 같이 개인키와 공개키가 생성이 됩니다.

개인키는 EC2에서 BitBucket에 접속할 때 사용되고, Public Key는 서버측에 등록합니다.

위의 bitbucket_rsa.pub가 공개키입니다.



위 그림과 같이 BitBucket에 들어가서 Profile_setting에 갑니다.

그러면 Security 항목에 SSH keys관리하는 탭이 있는데 이곳을 클릭합니다.

Add key를 눌러서 방금 만든 공개키를 넣고 추가해 줍니다.

참고로 RSA키가 2048비트여야 제대로 등록이 가능하니 주의하는 것이 좋습니다.

이제 Bitbucket에 SSH를 통해 접속할 때 위에서 생성한 Key를 사용할 수 있도록 설정을 해주어야 합니다.

cd ~/.ssh
vi config


파일을 위와 같이 설정해 줍니다.
bitbucket에 접속할 때 위의 private key를 사용하겠다는 의미가 됩니다.



위처럼 사용자(웹에서 접근한다면 apache 혹은 nginx)의 홈 디렉토리에 .ssh 폴더가 있어야 하며 안에는 config 파일과 개인키가 존재해야 합니다.

Repository Clone하기


git clone --mirror git@bitbucket.org:[bitbucket계정]/[repository명].git

위에서 보는 것처럼 Repository의 SSH 경로에 해당하는 주소에서 클론을 해와야 정상적으로 프로젝트를 가져올 수 있습니다.


.git 폴더가 Web Server의 루트 폴더(예를 들어 /var/www)에 존재하면 보안상 위험합니다.

따라서 git clone을 다른 폴더에서 진행하고 Work Tree(실제 작업하는 장소)를 웹서버 루트 폴더로 지정해줍니다.

GIT_WORK_TREE=/var/www git checkout -f master

성공적으로 되었다면 /var/www에 프로젝트 파일들이 모두 올라와있는 것을 확인할 수 있습니다.

업데이트가 있을 때는 다음과 같이 하면 됩니다.

[mirror clone으로 가져온 디렉토리] git fetch     //remote server에서 변경사항을 가져옵니다
[mirror clone으로 가져온 디렉토리] GIT_WORK_TREE=[work tree 경로] git checkout -f master

위의 명령어를 시전하면 됩니다. checkout을 실행하면 작업 트리에 마지막으로 커밋된 내용이 펼쳐지기 때문입니다.
다시 말하자면, fetch로 bitbucket 서버에서 변경된 내용을 모두 가져왔습니다. 그 후에 git checkout을 강제로 하면 마지막으로 커밋된 내용이 작업 트리에 적용됩니다. 즉, 서버의 업데이트 내용이 EC2 작업 폴더에 적용되는 것이죠.



Web Hook으로 자동 Deploy 설정하기


폴더 및 Repository 권한설정

Web Hook 스크립트를 실행하는 주체는 Nginx입니다.

Web Hook이 동작하는 흐름을 생각해보면 어떤 작업들이 필요한지 알 수 있겠죠?

동작 흐름은 다음과 같습니다.

  1. Local에서 원격 저장소로 Push를 한다.
  2. BitBucket에서 Push가 왔음을 인지하고 등록된 Web hook 스크립트(PHP)를 동작시킨다.
  3. Web hook 스크립트가 가동된다.
    1. 웹서버가 가동시킴. 누가 주체인지 알아야 함. ex) apache, nginx
    2. Push 요청시 실행할 PHP 스크립트가 웹루트 내 존재해야 함.
  4. 스크립트 내부에서 어떤 브랜치의 커밋인지 확인하고 업데이트를 할지 말지 여부를 결정한다.
  5. Project를 Clone한 프로젝트 폴더(위에서 생성한 .git 폴더를 의미합니다)로 이동한다.
    1. 웹서버가 Git Repository 접근/쓰기 권한을 가져야 함

  6. git fetch를 실행해서 업데이트 된 내역을 로컬 저장소에 불러온다.
    1. PHP 스크립트에서 shell_exec을 정상적으로 실행할 수 있도록 SELinux를 Permissive 상태로 돌려야 함
    2. 원격 저장소로부터 fetch를 할 수 있도록 웹서버 사용자 계정(apache나 nginx)으로 생성한 ssh private key가 있어야 함
    3. BitBucket 원격 저장소에 웹서버 사용자 계정으로 생성한 SSH Public Key가 등록이 되어있어야 함.

  7. GIT_WORK_TREE에 실제로 변경사항을 저장할 디렉토리를 지정해주고(보통은 웹 루트 디렉토리) git checkout -f를 수행합니다.
    1. 웹서버가 Work Tree 디렉토리에 접근/쓰기 권한을 가져야 함

  8. 변경사항이 로컬에 적용되었고 해당 내용을 로그로 만들어서 파일로 저장합니다.


자 이제 Web hook이 정상동작할 수 있게 선행되어야 할 작업들을 처리해볼까요?

nginx 사용자로 SSH key 생성하기

BitBucket 원격저장소와 통신하기 위해서는 SSH Key가 필요합니다.

Web hook을 사용해서 자동 배포를 하려면 웹서버(nginx나 apache)가 git 명령어를 실행해야 하죠.
그렇기 때문에 root 사용자로 SSH 키를 발급하고 web hook이 정상적으로 작동되리라고 생각하면 안됩니다.

처음부터 nginx로 SSH 키를 발급받는 것이 현명합니다.

sudo -u nginx bash      //nginx로 사용자 변경
cd ~/                   //홈 디렉토리로 이동 (/var/lib/nginx)
mkdir ./.ssh            //key를 찾을 때 .ssh라는 폴더를 찾습니다. 따라서 없으면 만들어줍니다
cd ./.ssh && ssh-keygen -t rsa      //새로 SSH 키를 발급받습니다.

passphrase는 건너뛰고 키를 생성하면 Priave key와 .pub가 붙은 Public Key가 발급됩니다.

앞서 얘기한 것처럼 config 파일을 만들어 주고 BitBucket 사이트에 가서 Settings → SSH에 Public Key를 등록해줍니다.


Git Repository 및 Work Tree Directory 권한 변경하기

위에서도 언급했지만 PHP 스크립트를 실행해서 git 명령어를 수행하는 주체는 웹서버가 됩니다.
git 명령어를 수행하기 위해 exec()이나 shell_exec()을 사용하는데 궁금하면 여기에 whoami라는 명령어를 수행해 보면 알 수 있습니다.

<?php 
$output = shell_exec("whoami"); //shell_exec은 명령어 수행 결과를 리턴
?>

아래와 같은 명령어로도 확인할 수 있습니다.

ps -eo "%U %G %a" | grep nginx


nginx의 경우 worker process가 스크립트를 처리합니다. 보면 사용자가 nginx로 되어있죠.

따라서 Git Repository와 Work Tree Directory에 nginx가 접근 / 쓰기가 가능해야 합니다.

아파치 사용자의 경우 apache나 www-data가 되겠죠?

아래의 명령어를 수행하면 해결됩니다.

chown -R nginx:nginx [git clone한 리파지토리 디렉토리]
chown -R nginx:nginx [소스들을 적용할 Work Tree 디렉토리]


nginx 사용자가 git 명령어나 파일 읽기 및 쓰기 권한을 사용할 수 있는지 확인을 해보는게 좋겠죠?

일단 nginx로 사용자를 변경합니다.

sudo -u nginx bash

그리고 Repository 디렉토리와 Work Tree 디렉토리로 이동해서 명령어를 수행해 봅니다.

git fetch       //권한이 제대로 되어있지 않으면 Permission Denied에러가 발생
touch example.log   //권한이 제대로 주어져있지 않으면 Permission Denied 에러가 발생


SELinux 설정 변경하기

SELinux 방화벽이 기본적으로 Enforcing 상태로 되어 있습니다.

따로 설정을 해주지 않는한 nginx가 PHP 스크립트에서 파일에 대한 읽기 및 쓰기가 되지 않습니다.
SELinux 때문이죠. 특히, exec을 활용한 명령어 수행이 대체적으로 막히게 됩니다.

따라서 Permissive 상태로 변경해주도록 합니다.

setenforce 0    //SELinux를 Permissive 상태로 전환
sestatus //SELinux 상태를 확인



Web hook 스크립트 작성하기


Bitbucket에서는 Push 요청이 들어오면 등록된 Web hook URL로 POST 요청을 보냅니다.

그렇기 때문에 정상적으로 수행이 되려면 스크립트가 Web Server의 Root 폴더 안에 있어야 합니다.

Nginx의 경우 루트 폴더가 다음과 같습니다.

/usr/share/nginx/html

그 밑에 deploy라는 디렉토리를 생성한 후 여기에 스크립트를 넣도록 하겠습니다.


 0))
    {
        echo "Update Success -> Deployed branch: " .  $branch . " Commit: " . $commit_hash . "\n";
    }
    else
    {
        echo "Update Failed -> Check Out Result : {$checkout_result} Fetch Result : {$fetch_result}";
    }
}
//Master 요청이 아니면 업데이트를 수행하지 않는다
else
{
    echo "Update Ignored -> Push from another branch, not master";
}
?>

위와 같이 간단한 스크립트를 작성합니다. 내용은 주석을 참고해 주세요.

요약하자면 master에 push 요청이 오면 git fetch명령을 수행하고 Work Tree에 해당하는 디렉토리에 checkout을 하는 내용입니다. 그리고 로그를 저장하죠.

이 스크립트를 위에서 얘기했던 웹 루트/deploy 폴더에 넣어줍니다. 그러면 BitBucket에서 URL을 사용해 스크립트를 실행할 수 있죠.

보안상의 위험이 있기 때문에 스크립트 이름은 해쉬값이나 추측하기 어려운 값으로 하는 것이 좋습니다.


Web hook 등록하기


이름을 적고 URL에 앞서 만들었던 스크립트의 위치를 적어줍니다.

여기서 주의할 점!!

HTTPS가 설정되지 않았는데 URL에 https://도메인/web-hook.php라고 적으면 당연히 안되겠죠?

이렇게 되면 Web hook 스크립트가 정상적으로 실행이 되지 않아 Net_ERR라는 에러가 발생합니다.

따라서 정확한 주소를 적어주세요.


등록하면 요청 목록을 확인할 수 있습니다.

Detail을 눌러보면 Request Body도 볼 수 있고 처리 상태 또한 확인할 수 있습니다.


Putty를 통해 EC2에 접속해서 Work Tree 디렉토리로 이동합니다.

Source 파일을 열어보면 최신 수정사항이 반영된 것을 확인할 수 있습니다.

이제 branch에서 작업 후 master로 Pull Request를 보낸 뒤 merge를 수행하게 되면 자동으로 EC2의 웹서버에 수정사항이 반영됩니다.

반응형