📝느낀 점
처음으로 AWS 배포를 진행하면서 만났던 에러와 느낀 점을 작성해 보려고 합니다.
처음에는 “괜찮은데?” 생각하며 진행하였으나, 중반부터 배포하기 직전까지도 에러와의 싸움이었습니다. 어느 순간에는 또 에러가 발생할까 봐, 겁먹었기도 하였으며, 하나하나 실수하지 않으려고 평소보다 더욱더 조심하며 진행하지 않았나 싶습니다. 지금 와 생각해 보면 어차피, 느리게 진행하거나 평소처럼 진행하거나 에러 나는 건 같았던 것 같은데, 왜 그랬나 싶네요 ㅎ.. 그리고 해당 에러를 해결하였다고 해서, 완전히 그 에러가 해결된 것이 아닐 수도 있기에, 다시 발생할 수 있는 경우의 수를 생각하며 테스트해 보는 과정에서 시간을 많이 사용하였던 것 같습니다. 성격 때문인지는 모르겠으나, 에러를 해결하였다고 해서 바로 넘겨 버리면 "완벽하게 해결하지 못했다?"는 기분이 들어 찝찝했습니다 ㅎ.. 그리하여 에러가 확실하게 해결되었는지?, 다른 상황에서 실행 시켜보면 에러가 발생하지 않는지? 이런 조건들을 다 검증, 즉 테스트해 보지 않으면 마음이 불편해서 하나의 에러를 만나더라도 검증하며 진행하였습니다. 그리고 처음 마주쳤던 에러들에 대해 어떻게 해결해야 될까? 막막함이 생겨 생각이 많았던 것 같습니다. 그 이유는 구글링을 해도 처음 마주하는 Route Table, Availability Zone, Security Group, 등등 모든 것이 처음 보는 용어들이라 어디서부터 기본지식을 쌓고 있어야 하는거지? 라는 생각이 컸기에 에러를 보는 게 두렵게 느껴지지 않았었나 생각 듭니다 ㅎ 저 같은 경우, 리눅스 환경에서의 작업은 처음 해봤기에 모든 것이 낯설었으며, 명령어는 어떤 것들이 있으며, 디렉터리는 어떻게 들어가야 하는 건지 디렉터리를 나올 때는 어떤 명령어를 사용해야 하는지 전혀 몰랐기에, 에러가 발생한다면 이 에러는 무엇인지?, 왜 발생 했는지?, 어떻게 해결해야 되는지? 감조차 오지 않았기에, 생각보다 힘들었던 첫 AWS 배포였지 않나 싶습니다. 하지만 이번 AWS 배포를 진행하며 힘들기도 하였지만, 이만큼 무언가를 배워간다는 생각도 하였습니다. 실력이란 것은 안 풀리는 문제에 직면했을 때 높아진다는 말을 항상 기억하고 있습니다. 힘든 이 시간을 견디면 다음 계단에 오를 수 있다는 믿음이 있기 때문에, 이번 배포 경험을 통해 또 한 계단은 올라왔다고 생각합니다. 이렇게 점점 한 계단씩 오를 것이며, 오늘보다 내일 더 성장할 수 있는 개발자가 될 수 있도록 하겠습니다.
추가로 이번 aws 배포를 통해 기록하였던 글들 입니다!
- 배포를 진행하면서 알게 되었던 AWS 용어 정리 하였던 글
- 클라우드 컴퓨팅 / 리전(Region) / 가용영역(Availability Zone) : https://alstjr706.tistory.com/358
- EC2란? / RDS란? : https://alstjr706.tistory.com/359
- 라우트 테이블(Route Table) / 보안 그룹(Security Group) : https://alstjr706.tistory.com/360
- VPC란? / 보안 그룹(Security Group)이란? : https://alstjr706.tistory.com/361
- 사설 ip, CIDR / 서브넷이란? : https://alstjr706.tistory.com/362
- 리눅스 환경에서 사용하고 알게 되었던 명령어들을 기록한 글
- AWS 환경 EC2 서버로 접속하기 등 환경 설정 방법 등을 기록한 글
- EC2 서버로 접속하기 : https://alstjr706.tistory.com/352
- IntelliJ에서 RDS에 접속하기 : https://alstjr706.tistory.com/353
- EC2에서 RDS 연결 확인 방법 : https://alstjr706.tistory.com/354
AWS를 선택한 이유는?
클라우드에서는 기본적으로 지원하는 기능(모니터링, 로그 관리, 백업, 복구, 클러스터링 등)이 많이 있습니다. 그렇기에 개인이나 소규모일 때 개발에 좀 더 집중할 수 있다는 장점이 있습니다. 또한 많은 기업이 AWS로 사용 중으로 국내에서는 AWS 점유율이 높습니다. 그 이유로는 아무래도 AWS는 사용한 만큼만 지불하면 되기 때문에 투자 비용, 관리와 운영에 투입되는 비용이 확실히 절감되지 않을까 생각이 듭니다. 또한 취준생인 저에게 첫 가입 시 1년간 대부분 서비스가 무료이기에 선택하게 된 이유가 있습니다.
"AWS 프리티어로 무중단 배포가 불가능합니다"
AWS RDS 사용
백엔드에서의 가장 중요한 것은 애플리케이션 코드를 작성하는 것도 중요하겠지만 그만큼 중요한 것이 데이터베이스를
다루는 것이라고 생각합니다. 데이터베이스를 설치하고, 운영하고, 백업하는 일들이 상당히 까다롭고 위험하기에 때문에 AWS에서는 데이터 알아서 모두 지원하는 클라우드 기반인 관계형 데이터 베이스인 RDS(Relational Database Service)를 제공합니다. 추가로 조정 가능한 용량을 지원하여 예상치 못한 양의 데이터가 쌓여도 비용만 추가한다면 정상적으로 서비스가 동작한다는 장점도 있습니다.
진행하면서 마주쳤던 에러들에 대한 설명
첫 번째 에러.
Could not create connection to database server. Attempted reconnect 3 times.
가장 시간이 오래 걸렸던 에러였습니다. 해당 에러를 해석해 보자면, 데이터베이스 서버에 대해 연결할 수 없다는 것이었습니다. 그리고 연결을 3번이나 시도했는데 실패했다.. 음... 시작부터 이 문제에 대해 어렵게 느껴졌던 것 같습니다. 일단 데이터베이스 서버에 연결할 수 없는 경우의 수가 많다고 느껴졌기 때문입니다. 지금까지 EC2 환경 설정, RDS 환경 설정을 잘하고, 문제가 없다고 생각하고 넘어갔던 부분들이 해당 에러를 통해서 검사받는 듯한 기분을 느꼈습니다. 여기서 짚고 넘어가는 듯이 해당 에러를 해결하는데, 정말 많은 시간을 치러야 했습니다. 그리하여 이 문제상황을 해결하기 위한 여러 시도를 잘 풀어 설명해 드리도록 하겠습니다.
첫 번째 문제해결을 위한 시도. MYSQL 버전입니다. MYSQL 5.5.45+, 5.6.26+, 5.7.6+ 부터는 ssl 접속이 default라서 useSSL=false를 명시하지 않으면 자동으로 ssl 접속을 시도한다는 것입니다. 즉, 이게 왜 문제라고 감지하였냐면, 인증 오류가 문제로 발생할 수도 있다고 생각했기 때문입니다. MYSQL 서버가 SSL을 사용하도록 설정되어 있고, 클라이언트에서는 SSL을 사용하지 않을 경우 서버는 암호화된 연결을 요구하게 되는 것입니다. 그렇기에 서버와의 연결이 실패할 수도 있다고 생각하였습니다.
두 번째 문제해결을 위한 시도. 방화벽 TCP 인바운드 규칙 설정입니다. 에러 문구에서 알 수 있듯이, 데이터베이스 서버와 연결을 할 수 없기 때문에 발생한 것으로 네트워크 문제가 될 수 있지 않을까? 하여 문제를 감지할 수 있었습니다. 환경 설정에 들어가 필요한 TCP 포트가 허용되고 있는 확인 하여 봤습니다. MYSQL은 기본 3306번 포트이며, 데이터베이스 서버와의 통신에 필요한 포트는 열려 있었기에 방화벽 문제도 아녔습니다.
하지만 지금와 생각해보니, 인바운드 규칙을 체크하는게 의미가 없다는 뒤늦게 깨달았습니다. 이유는 내 컴퓨터인 RDS MY SQL 3306 포트로 연결하는 것이기 때문입니다. 즉, 저는 아웃바운드를 생각하지 못했던것 같습니다. 또한, 내 컴퓨터에서 RDS MYSQL 3306 포트로 연결할 때 컴퓨터 포트는 3306이 아닌 특정 범위 안의 랜덤 포트를 사용 한다는 것을 알게 되었습니다.
그렇게 사용하게 되는 이유를 말씀드려 보자면, 로컬 컴퓨터에서 RDS MYSQL에 접속할 때, 로컬 컴퓨터는 보통 3306 포트를 사용됩니다. 그리고 RDS 입장에서 생각해본다면 RDS MYSQL 인스턴스는 보통 3306 포트를 열어두고, 이 포트를 통해 클라이언트와 통신을 진행하게 됩니다. 그렇다면, 내 PC에서 RDS로 접속할때 내 PC는 3306 포트가 아니라 랜덤 포트를 사용하는 것입니다. 이렇게 하여 좋은점은, 연결이 끝나면 다시 다른 연결에 사용될 수 있도록 동적으로 할당됩니다. 이렇게 함으로써 여러 연결이 동시에 사용되거나 충돌하지 않는다는 장점이 있을 수 있기에 랜덤 포트를 사용하는 것도 좋은 방법이라고 생각했습니다. (현재 밑에 있는 사진은 동적 포트를 사용하기 전 입니다!)
세 번째 문제해결을 위한 시도. 패스워드와 사용자 계정 설정 문제가 될 수도 있겠다고 판단하였습니다. 그 이유는, 잘못된 사용자 계정 또는 패스워드로 설정 있거나 또는 오타가 있을 경우에서 데이터베이스 서버에 연결을 시도할 경우 접속이 거부 될 수 있는 근본적인 원인일 수도 있겠다고 생각하였기 때문입니다. 또한 접속 권한 문제일지 생각도 해보았습니다. 하지만 둘 다 아녔습니다. ㅎ..
네 번째 문제해결을 위한 시도. 결론부터 말씀드리자면 네 번째 시도에서 에러를 해결할 수 있었습니다. 로컬 DB 연결 정보인 application.yml 파일과 서버 DB 연결 정보인 application-prod.yml 파일에 대해 정확히 모르고 있었다는 것입니다. 즉, spring.profiles.active=이라는 개념을 자세히 모르고 있었기에 문제상황을 마주하게 되었다고 말씀드릴 수 있을 것 같습니다. spring.profiles.active 대해 설명해 보자면, 애플리케이션에서의 활성화된 프로파일을 지정하는 데 사용되는 속성입니다. 기본값으로는 spring boot 는 application.properties 또는 application.yml 파일을 프로퍼티로 설정하게 되면서 애플리케이션이 특정 환경에서 동작하도록 설정할 수 있다는 것입니다.
그리하여 spring.profiles.active=prod 설정해 줌으로써 application.yml 파일이 아닌 application-prod.yml가 실행되게 되는 것입니다. 결론은 어떤 yml 파일을 실행시킬 것이냐?에 중점을 두면 된다는 것입니다. 인텔리제이 같은 경우는 디폴트 값으로 application.yml를 실행시키기에(로컬 DB 계정으로 연결되어 있다면) 로컬 DB 계정이 동작할 수 있고, 서버 환경에서는 --spring.profiles.active=prod' 명령문을 실행시키기에 속성이 prod가 되어 application-prod.yml의 파일의 명을 실행시키게 되는 것입니다. (관련 사진은 밑에 있습니다!)
두 번째 에러.
FAILURE: Build failed with an exception.
EngageNexusApplicationTests > contextLoads() FAILED
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:98
Caused by: org.springframework.beans.factory.BeanCreationException at AbstractAutowireCapableBeanFactory.java:1804
Caused by: org.hibernate.service.spi.ServiceException at AbstractServiceRegistryImpl.java:284
Caused by: org.hibernate.HibernateException at DialectFactoryImpl.java:100
1 test completed, 1 failed
./gradlew clean build를 진행하려고 하였으나, 발생하게 된 에러 입니다. 해당 에러는 테스트 실행 중 실패 관련 에러이며, 빌드에 실패하였을 경우 나오는 에러 메시지 입니다. 실제 테스트 실패 내용은 다음 부분에 나타나 있었습니다. 인텔리제이에 있는 테스트 메서드인 contextLoads()가 실패했다는 내용인데, "contextLoads()" 테스트가 왜 실패했는지? 곰곰이 생각하여 봤습니다. 몇가지의 경우의 수가 생각이 났습니다.
첫 번째로 환경 변수가 맞지 않아 이런 에러가 발생하나 싶은 생각을 해보았습니다. 하지만, 에러 발생 시점을 기준으로 application.yml 파일이 하나뿐이었기도 하였고, 내부 필요한 환경 변수와 맞지 않을까 라는 생각도 해보았지만, DB 커넥션 연결을 잘되어 있기에 아니라는 생각을 하였습니다.
두 번째로 실행 권한에 문제가 있을까 생각도 하게 되었습니다. 왜냐하면 리눅스나 유닉스 계열 운영체제에서는 실행 파일에 대한 실행 권한이 없으면 파일을 실행할 수 없기 때문입니다. 그리하여 해결 방법으로는 root 계정으로 build를 진행하니 해결되었습니다. 이렇게 하면 왜 해결 되었을까? 루트 계정으로 실행하였을 때, 에러가 해결된 이번 상황 같은 경우는 환경 변수 문제라고 생각 들었습니다. 원인 : 실행 환경에서 변수가 사용자에게 부여되지 않아 필요한 환경 변수를 찾지 못하는 경우가 아닐지 생각이 들었습니다.
그리하여 해결 방법으로는 root 가능하였지만, 더 좋은 방법으로는 필요한 환경 변수를 설정하거나 빌드나 실행 시에 환경 변수로 전달하는 방식이 더 좋을 방법일 것 같습니다. 이처럼 build를 진행할 때마다 root 계정을 가져오는 방법 또한 좋지 않다고 생각하였습니다. 그 이유는 보안상 좋지 않다고 판단했습니다. 루트는 모든 부분에 대해 접근이 가능하기에 필요 이상으로 높은 권한을 가지고 있다고 판단되어 보안에 취약하다고 생각하였습니다.
위 에러와 비슷한 경우
비슷한 경우의 접근 권한 에러를 테스트해 보고 싶기에 권한이 없는 계정으로 앞으로 사용하게 될 실행 파일들을 실행하여 봤습니다. 이렇게 진행하게 된 이유는 해당 에러가 발생하였을 때, 똑같은 상황의 에러가 발생하는 걸 막기 위함도 있으며, 해당 에러를 해결했다고 해서 바로 매듭을 짓고 싶지 않았기 때문입니다. 즉, 문제를 해결하였는데, 제가 해결한 방법이 정상이 아닐 수도 있기에, 다른 케이스를 통해 확인하는 방법으로 이런 방식을 생각하여 봤습니다. 그리하여 마주쳤던 에러 비슷한 에러가 발생하였습니다. 그리하여 일반 사용자에게 chmod +x./gradlew 파일 권한 변경 명령어를 통해 권한을 부여하게 되었습니다. 그렇게 문제를 해결할 수 있게 되었습니다. 여기서 chmod가 뭐지? 싶어 구글링을 해봤는데 chmod는 "change mode"의 줄임말이었습니다. ㅎ..
세 번째 에러.
-bash:./gradlew : No such file or directory
이 에러에 대해 메시지만 보고는 gradlew 파일을 찾을 수가 없다는 내용인데, 이건 또 무슨 소리일까 싶었습니다 ㅋㅋ...
잘 있는 gradlew 파일이 없다니, 왜? 곰곰이 생각한 결과 실행 권한이 없을 경우에는 해당 파일을 실행시킬 수 없기에 파일을 찾을 수 없다는 게 아닐까? 생각하며 권한이 높은 계정으로 진행하여 봤더니 문제가 해결되었습니다 ㅎㅎ 에러는 순간순간 뜰 때마다 당황스럽긴 하네요 ㅎㅎ..
네 번째 에러.
fatal: not a git repository
git pull을 진행했는데, 이런 에러 발생하였습니다. 어떤 상황의 문제 이길래 이런 에러를 마주하게 되었을까 고민하게 되었습니다, git 저장소가 아니라고 하는데, 음 분명 깃허브 레포지토리 주소랑 제대로 가져왔는데 말입니다. 그런데, 왜 안 될까...? 곰곰이 생각을 해보다 리눅스는 계층 구조를 가진 운영 체제라는 것이 문득 생각났습니다. 즉, 파일 시스템이 계층적 구조를 가지고 있기에 디렉터리 파일 또한 트리 구조로 되어 있다는 것을 생각하게 되었습니다. 그렇다면 git 레포지토리를 땡겨 오는 것과 별개로 현재 내가 위치하고 있는 디렉토리를 어떻게 확인할 수 있지? 생각해 봤으며 이 부분이 지금 나의 문제점이라는 것을 감지하게 되었습니다.
그리하여 구글링을 통해 리눅스의 cd 명령어 ls명령어를 알게 되었고, 문제를 해결할 수 있게 되었습니다. 결국 git 명령을 실행할 때 현재 디렉토리나 부모 디렉토리에서 .git 디렉토리를 찾지 못하는 상황이었기에, 이 문제를 어떻게 마주하게 되었는지에 대한 생각을 하지 않았다면, 한창 헤매고 있었지 않았을까 생각합니다. 그렇기에 어떤 행동을 했으며, 어떤 문제 상황인지 확실히 인지하고, 그렇다면 이게 왜 문제인지 생각해 볼 수 있다면 해결할 수 있는 문제였던 것 같습니다. 결론은 git pull을 진행할 때, 디렉토리 밖인 즉, 예를 들어, 바탕화면에서 계속 git pull을 진행하고 있었던 것입니다. ㅎ 바보 같은 모습이지만 이렇게 기록하며 두 번 다시 까먹지 않기 위해 기록해 두도록 하겠습니다!
다섯 번째 에러.
there is no tracking information for the current branch
해당 에러는 git에서 현재 브랜치에 대한 정보를 추적할 수 없을 경우 발생하는 에러였습니다
친절하게 에러 문구를 보면 알 수 있는였지 않았나 싶습니다. 해당 에러는 현재 제 깃허브 레포지토리의 브랜치를 develop에서 작업하고 있었으나, 현재 브랜치가 master로 변경되어 있어서 이런 에러가 발생하였던 것 같습니다. 에러가 발생했다고 당황하지 말고 천천히 에러에 문구에 해답이 있다는 걸 다시 한번 깨닫게 해주는 에러였던 것 같습니다.
여섯 번째 에러.
error: pathspec 'develop' did not match any file(s) known to git
해당 에러 메시지는 Git 명령에서 'develop'이라는 경로나 브랜치가 존재하지 않는다는 것을 말합니다. 지금까지 잘 사용하던 develop 브랜치가 존재하지 않는다니? 정상적으로 동작해야 할 부분이 되지 않고, 에러가 발생할 때 정말 막막함이 찾아오는 것 같습니다. 해당 이름의 브랜치가 git 저장소에 존재하지 않다? 곰곰이 생각해 봤던 에러였던 것 같습니다. 그리고 브랜치 조회도 안 되었습니다. 이유가 뭘까? 왜 가지지 않을까? git checkout이 뭘까?부터 생각하여 봤으며 실마리는 그렇다면, 회사에서 여러 사람이 한 레포지토리에서 git checkout을 하려고 할 건데 충돌이 나지 않을 수 있게 하는 방법은 뭐지? → 그렇다면 git checkout의 조건은 무엇일까? 에 도착하게 되어 해답을 찾을 수 있었던 것 같습니다.
생각을 말씀드려 보자면, 항상 로컬 저장소에서는 리모트 저장소로부터 최신 변경 사항을 가져와야 할 것입니다. 왜? 그래야 다른 팀원의 코드에 변경 사항이 생긴다면 그 변경 사항을 가져와서 최신 상태로 유지해야지 팀원들과의 협업이 문제없이 진행될 것이라고 생각하며, 또한 개발 작업을 수행하는 동안 로컬 저장소가 리모트 저장소와의 동기화가 되어 있지 않으면 프로젝트의 일관성이 무너질 수 있기 때문입니다. 그렇기에 git fetch 명령어를 통해 브랜치를 최신화해줌으로써 문제를 해결할 수 있었으며, 일관성 유지 함으로써 원활한 협업이 가능해진다고 생각하였습니다. 팀원들 간의 충돌이 방지할 수 있기 때문입니다. 그렇기에 항상 최신 상태를 유지해야 한다고 생각했습니다.
일곱 번째 에러.
fatal: 'origin' does not appear to be a git repository
현재 프로젝트에서 깃헙 저장소에 대한 정보를 알고 싶어 명령어를 날렸지만, 에러가 발생하였습니다. 이 에러 메시지는 Git이 'origin'이라는 이름의 원격 저장소를 찾을 수 없을 때 발생하는 에러입니다. 허... 왜 찾을 수 없다? 상황을 한번 정리하여 보았습니다. 일단, 로컬에 미리 디렉토리를 만들었고, 이 로컬 디렉터리와 git 원격 저장소에 연결이 되어 있는데, 음... 이 부분은 구글링 하며 원인을 찾게 되었습니다. “연결된 것을 삭제하고 다시 연결하면 해결된다는 글을 봤었습니다” 왜 그렇지?? 무슨 상관이 있길래 그런 거지? 머릿속에는 온통 왜? 뿐이었습니다.
생각해 보면 이 에러는 로컬 저장소에 연결된 origin 리모트 저장소가 올바르게 저장되어 있지 않았기에 발생한 오류였는데, 여기서 조금만 더 생각해 보면 실마리를 잡을 수 있었습니다. 그 이유로는 저장소의 상태를 최신화한다는 것입니다. 로컬 저장소에 git remote add 명령어로 리모트 저장소를 추가하면 git은 해당 저장소의 상태를 기록하게 됩니다. 만약, 상태가 올바르게 갱신되지 않았거나 손상되었을 경우를 생각해 본다면 저장소를 삭제하고 다시 추가함으로써 git은 해당 저장소를 새롭게 확인하고 설정이 가능하게 되기 때문입니다.
즉, 이 과정에서 기존의 설정 문제를 해결할 수 있기에 연결된 것을 삭제하고 다시 연결하는 이유라고 볼 수 있을 것 같습니다. 그리하여 git remote remove minseok 명령어를 사용하여 기존에 원격 저장소 연결을 삭제하고, → git remote add origin 깃주소/. git 명령어를 사용하여 다시 연결하여 마지막으로 git remote -v로 확인까지 하여 에러를 해결할 수 있었습니다.