기존에는 배포할때 로컬에서 작업해서 ftp로 실서버에 덮어씌우는 방식으로 진행함. 그러나 파일이 많고 서비스 규모가 커지면 교체할 파일이 많아지므로 배포 자동화를 진행하는게 좋음.
컴파일
컴파일이란 프로그래밍 언어를 기계가 이해할 수 있는 기계의 언어로 번역하는 것이다.
개발자의 편의를 위해 작성한 java와 같은 프로그래밍 언어는 기계가 이해할 수 없기 때문에 프로그래밍 언어를 컴파일러가 컴파일 해 기계가 이해할 수 있는 언어로 번역해준다.
빌드
다음은 컴파일된 기계의 언어를 사용자에게 보여주기 위해 빌드하여 완성된 상품, 소프트웨어 가공물로 만든다.
java에서는 maven, gradle과 같은 빌드 도구를 이용하면 컴파일과 함께 소스코드 파일을 .jar, .war 와 같은 산출물로 변환하는 빌드도 함께 할 수 있다.
배포
이렇게 만들어진 산출물을 각각의 서버에서 동작하도록 하여 최종 결과물을 보여주는게 배포이다.
CI란?
지속적 통합이라는 뜻으로 개발을 진행하면서도 품질을 관리할 수 있도록 하는 것으로 여러 명이 하나의 코드에 대해서 수정을 진행해도 지속적으로 통합하면서 관리할 수 있음을 의미
CI가 나오기 전까지는 개발을 끝마치고 배포가 되어야만 코드에 오류는 없는지, 올바르게 동작하는지를 검증하며 코드 품질을 관리할 수 있었다. 하지만 개발자가 직접 코드를 병합하고 빌드, 테스트를 검증하는 것은 시간이 많이 소요되므로 이를 자동화하면 개발자가 빌드와 테스트를 직접 하지 않고도 수정한 코드를 브랜치에 병합하기만 하면 자동으로 빌드와 테스트를 검증되는 장점이 있다.
개발자가 단위별로 구현한 부분을 병합할 때마다 자동화된 빌드와 테스트가 트리거되어 실행된다. 결과를 통해 우리는 어떤 부분에서 문제가 있는지 배포 전에 확인할 수 있고, 배포가 완성된 후에야 버그를 수정할 수 있던 기존의 문제를 빠르고 정확하게 해결할 수 있다.
✍️CI단계
step 1) feature브랜치에서 작업한 코드를 디벨롭으로 머지.
step 2) 머지된 코드가 정상적으로 빌드되고 작동하는지 검증.
step 3) 오류가 있다면 수정해서 다시 위의 단계 반복.
CD란?
지속적 배포는 지속적 통합을 통해 성공적으로 병합된 내역을 저장소뿐만 아니라 사용자가 사용할 수 있는 배포환경까지 릴리즈하는 것을 의미한다.
지속적 배포를 다양한 방법을 통해서 구축할 수 있지만 대표적으로는 Jenkins가 있다. 그러나 오늘은 AWS Code Deploy를 통한 구축 방법에 대해 정리하고자 한다.
비트버킷 원격 레포지토리에 코드를 올리면 빌드를 진행한다. 비트버킷에서는 클라우드 기반 도커에서 빌드를 진행한다. 그러면 실제 라이브 서버에 빌드를 해서 어플리케이션을 실행하는건 aws의 Code Deploy 서비스를 통해서 진행한다.
비트버킷에서 aws의 Code Deploy로 빌드된 소스를 보내주면 Code Deploy에서 라이블 서버로 소스를 보내서 실행한다.
spring boot application 의 scripts폴더에 sh 파일들의 명령어를 통해서 jar파일을 실행하는 코드를 만들어서 bitbucket pipeline설정파일인 appspec.yml에 적힌 명령어를 통해서 실행한다.
결론적으로 개발자가 배포를 위해 하는 일은 결국 비트버킷 원격 레포지토리로 코드를 푸쉬하는 것으로 하는 것이다. 푸쉬 이후에는 비트버킷에서 도커를 사용해서 빌드하고 코드 디플로이를 활용해서 실 서버로 반영해주기 때문에 빌드 과정에서 발생할 에러를 최대한 방지할 수 있다.
이때 사용하는 CICD툴은 비트버킷 말고도 github에서도 비슷한 클라우드 서비스가 존재함. 또 위에서 언급한 Jenkins도 유명한데 이 경우에는 클라우드 서버를 사용하는게 아니라 서버를 직접 구축해야함. 직접 세팅해줘야하는 번거로움이 있지만 커스텀이 가능하다는 장점이 있음.
step1) bitbucket 파이프라인에서 빌드 진행
bitbucket-pipelines.yml파일에서 파이프라인에서 어떤 작업을 할지 설정함.
브랜치마다 작업을 다르게 진행할 수 있음. 마스터면 실서버로, 릴리즈면 스테이지로 배포하도록 설정가능함.
원격 레포지토리로 푸쉬가 일어나면 안의 작업들이 step으로 실행됨.
처음에는 클라우드 도커 이미지를 설정해줘야함. 자바의 경우 gradle로 jdk 버전을 명시하면 됨.
script에 있는 명령어가 빌드 명령어임. 빌드가 완료되면 jar파일이 생성됨. 컨테이너 안에서 바깥으로 빌드된 파일을 실서버로 넘기는 것.
artifact는 다음 스텝으로 넘길 작업 결과물을 의미. root.jar가 빌드 완료된 파일.
실서버에서 작업을 수행해야하기 때문에 코드디플로이에서 볼 파일이 appspec.yml에 명시되어 있음.
deploy에서 위의 세개의 작업 결과물을 zip파일로 압축함.
pipelines:
branches:
브랜치 이름:
- step:
name: Setup
image: 빌드 도구 : jdk버전
script:
스크립트 명령어 작성
artifacts:
- root.jar
- appspec.yml
- scripts/*.sh
- step:
name: Deploy
image:
script:
step 2) aws code deploy에서 배포 작업
코드 디플로이에서 파일을 받아서 배포 작업이 시작됨. 코드 디플로이에서 배포 그룹을 만들어서 세팅을 해둠. 어떤 서버에 배포를할건지 세팅함. 비트버킷에서는 어떤 배포 그룹으로 보낼건지 지정함. 코드 디플로이에서 이걸 받아서 검증후 스크립트 실행. 이 과정이 완료되면 bitbucket-pipeline에서 success가 뜸!
이때 appspec.yml파일에서 어떤 스크립트를 실행시킬건지 명시해둠.
start.sh, stop.sh, health.sh, profile.sh, switch.sh등
무중단 배포란 말 그대로 서비스가 중단되지 않은 상태(zero-downtime)로, 새로운 버전을 사용자들에게 배포하는 것을 의미
빌드한 jar파일을 적용시키려면 바뀐 파일로 다시 어플리케이션을 실행해야하기 때문에 어플리케이션을 껐다 켜야한다. 그런데 그렇게 되면 배포 시에 사이트가 죽기 때문에 이걸 막기 위한 방법이 무중단 배포이다.
방법 1) 두개 이상의 서버를 돌린다.
이를 위해서 필요한 전제조건이 두개 이상의 서버의 확보이다. 서버를 두개로 돌리고 하나의 test.com이라는 도메인을 치고 들어왔을때 맨 처음에는 a서버를 바라보게 한다. 그리고 두번째 배포는 b서버를 바라보게 한다. 그래서 문제 없이 잘 돌아간다면 도메인의 연결을 b서버로 바꾼다. 이때 a에서 b로 바뀌는 작업은 순식간에 일어나기 때문에 사용자는 중단점을 느낄 수 없다.
그러나 이 방식의 단점은 서버를 두대를 돌려야한다는 것이다. 같은 작업을 하는 서버를 두개나 두어야하므로 비용이 든다. 물론 b서버를 바라보게 하면 a서버는 돌아가지만 트래픽이 발생하지 않아서 큰 비용이 들지 않을 수도 있다. 그러나 기본 비용은 소모된다. 또한 a서버에서 b서버로 바꾸는 과정이 DNS캐시로 인해 시간차가 발생할 수 있다는 이슈가 있다.
방법2) 두개 이상의 어플리케이션을 돌린다.
위의 단점을 커버하기 위해 서버를 한대 두고 어플리케이션을 두개 돌리는 방법도 있다. 스프링부트 기본 포드를 8080으로 어플리케이션을 하나 실행한다. 이때 test도메인으로 들어오면 8080포트로 돌아가는 어플리케이션으로 연결해둔다. 두번째 배포에서는 다른 포트로 어플리케이션을 하나 더 만든다. 8081 포트를 하나 더 두고 어플리케이션을 실행해서 큰 문제가 없다면 8081번으로 도메인을 스위칭해준다. 그리고 다시 3번째 업데이트에서는 8080번 포트를 사용한다. 이런식으로 두개의 어플리케이션을 왔다갔다 실행하면서 도메인과 연결한다. 이러한 세팅은 nginx로 한다
nginx는 웹서버로 클라이언트의 요청을 처리하는 소프트웨어이다. nginx에서 프록시 변수를 받아서 localhost:8080, localhost:8081을 변수로 써서 배포시마다 바꿔서 포워딩을 시켜준다. 도메인은 하나인데 포트 번호가 두개인 위의 경우, 사이트 등록시 포트번호도 붙여야하니까 로컬에서만 포트 번호를 다르게 쓰도록 변수마다 포워딩을 다르게 하는 리버스 프록시를 사용한다. (단, 포트 번호를 바꿔주는건 nginx에서 하지 않고 다른 곳에서 한다!)
위에서 잠깐 언급했던 code deploy시에 스크립트를 사용하는데 scripts폴더 안에 있는 파일 중 배포를 시작하면 우선 stop.sh파일을 사용한다. 현재 라이브중인 포트 번호를 확인해서 남는 포트를 죽인다. 그리고 남는 포트로 다시 어플리케이션을 실행하는데 이때 health.sh를 실행해서 요청에 응답을 제대로 하는지 확인하고 이후 switch.sh파일을 통해서 스위칭을 진행한다. service-url.inc파일에서 service.url을 바꿔주고 nginx를 다시 시작해준다.
여기서 잠깐💡
프록시란 인터넷 접속시 보안상의 문제로 직접 통신을 주고 받을 수 없을 때 그 사이의 중계기로서 대리로 통신을 수행하는 기능을 뜻한다.
포워드 프록시 : 클라이언트와 인터넷 사이의 영역. 클라이언트가 어떤한 정보를 요청하면 포워드 프록시가 이를 대신 받아서 서버에 전달. 이후 서버의 응답 또한 포워드 프록시가 대신 받아서 클라이언트에 전달.
리버스 프록시: 인터넷과 백엔드 사이의 서버 영역.
완벽할 것 같던 무중단 배포에도 문제점이 발생할 수 있다. 사용자가 결제를 하고 있는 상태를 가정해보자. 주문서를 생성하고 카드 결제를 진행중인데 스위칭이 되어 버린다면?😱
만약 서버에서 세션으로 데이터를 관리하고 있다면 어플리케이션 마다 세션 정보가 독립되어 있으므로 포트 스위칭이 된다고 해서 각 어플리케이션의 세션 정보까지 넘어가지 않으므로 결제 이후 단계로 넘어갈 수 없다는 문제가 생긴다.
이를 위해서 양 어플리케이간의 세션을 연동하는 방법으로 해결하기도 한다. 세션에서 작업이 완료될 때까지 기다렸다가 스위칭을 해주는 방식을 쓰기도 한다!
aws ec2
클라우드 서버. 물리적 서버가 아님. 서버는 다 리눅스로 되어 있음.
서버를 인스턴스라고 함. os랑 티어 유형을 선택해서 사양에 따라 비용 부과.
저장장치는 따로 만들어서 붙여줘야함. storage보면 하드디스크 볼륨 붙여서 사용.
rds
db를 여기서 관리. 데이터베이스 엔진 선택해서 만든다.
서버에 직접 디비를 설치해서 쓸 수 있지만 요즘은 보통 aws 쓰면 rds에서 디비 만들어서 쓴다.
백업
인스턴스 서버 자체를 백업해서 롤백하도록 해두고 db 백업을 따로 해둠.
서버는 일주일에 한번. 디비는 매일. s3는 데이터 저장소. 이것도 매일 백업.
lamda
코드를 짜서 실행할 수 있는 서비스. 서버리스를 보통 람다로 함수로 하나씩 만들어서 스프링 어플리케이션 없이 필요한 기능을 외부에서 사용할 수 있는 엔드포인트를 따서 람다 서비스 주소로 api 요청을 보내서 작업할 수도 있음. 배치 스케줄링 작업을 위해서 사용. api요청하기 위한 람다함수를 만들어서 사용. 부재중 알림톡 같은거.
로그인 api요청해서 토큰확인해서 부재중 알림톡 보내는 api를 요청해서 알림톡 보내는 것.
이벤트 브릿지
스케줄러는 이벤트브릿지 사용
스케줄러를 만들어서 람다 함수를 정해진 시간에 주기적으로 실행. 스프링배치에서도 스케줄러 지원. aws 통하지 않고 api서버에서 직접 실행하는것. 그렇게 되면 람다함수도 필요 없음.
람다 함수에서 기능마다 함수로 구현해두고 스케줄러에서 해당 람다함수를 호출하는
오토스케일링
사용자가 많아지면 cpu사용량이 올라가면 서버 사양 올려주는 것.
로드밸런서
트래픽 관련. 같은 인스턴스를 여러개 만들어서 트래픽을 분산시키는 것. 서버를 여러개 만들어서 같은 옵션이 들어와도 분산시키는 것.
루트 53
도메인 관리. 인프라 네트워크 관련 내용.
사용자가 도메인을 치고 들어오려면 몇단계가 걸림. 브라우저가 도메인에 연결된 dns서버로 이동. dns는 도메인에 연결된 실제 ip를 관리하는 서버. dns는 서버의 ip정보를 가지고 있음. ip주소를 가지고 접속함. 도메인으로 사이트 접속하려면 dns가 필요함. 도메인과 연결될 ip주소를 관리하는게 루트 53
도메인을 등록해야하는데 이걸 도메인을 판매하는 곳에서 구입해야함. 도메인에서 접속하면 어떤 서버로 이동할지를 등록. dns서버 역할을 하는 것. 서브 도메인 별로 다른 서버로 연결시킬 수도 있음.
aws certificate manager
https는 보안 프로토콜. 이걸 적용하려면 보안 인증서가 필요함. 인증서를 서버에 설치를 해야함. aws에서는 위의 매니저에서 인증서를 요청해서 발급받아서 로드밸런서에서 인증서를 등록해야함. aws니까 이게 가능한데 물리적 서버에서는 파일을 직접 서버에 올려서 적용해야함. 개인정보를 취급하는 사이트는 다 적용해야함. 1년마다 갱신해줘야함. aws는 인증서 생성시 자동 갱신 옵션이 있음.
파라미터 스토어
디비 접속 정보
어플리케이션 내에 하드코딩해서 쓰면 접속정보가 털릴 위험이 있으니까 정보를 파라미터스토어에 올려두고 유저네임이랑 비번을 가져오는 것.
application.yml 파일에 보면 aws parameter sotre에 연동하는 부분이 있음. 여기서 db username, password를 파라미터 스토어에서 가져오도록 하는 것.