안녕하세요. LINE Pay 팀에서 일하고 있는 유재훈, 서동규입니다. Pay SRE 팀은 LINE Pay 서비스의 개발 환경과 운영 환경 사이에 존재하는 음영 지역을 효율적으로 커버해서 LINE Pay 전체 시스템의 신뢰도를 높이고 성능을 극대화하기 위해 다양한 활동을 진행하는 팀입니다. 이번 글에서는 지난 2년 동안 Rundeck과 Ansible을 활용해 보안을 강화하고 서버 작업을 자동화했던 경험을 공유하려고 합니다.
작업 배경
LINE Pay는 2014년에 서비스를 출시한 후 빠르게 성장하면서 다양한 비즈니스 도메인을 보유하게 됐고, 이를 운영하기 위해 수천 대 규모의 서버 자원을 활용하고 있습니다. 그동안 사용자의 다양한 요구를 수용하기 위해 비즈니스 도메인 개발에 집중한 결과 다양한 결제 서비스를 출시할 수 있었는데요. 이로 인해 연동 시스템 간 복잡도가 증가하면서 운영 환경의 인프라 작업 빈도가 증가했고, 변경 영향도를 예측하기 어려워져 운영 비용이 증가했으며, 이에 따라 운영 중 사고 발생 가능성과 서비스의 품질 저하 가능성이 높아지고 있었습니다. 결과적으로 서비스의 성장 속도와 안정성의 균형을 맞추기 위해 관리 방식과 체계를 개선할 필요가 있었습니다.
또한 전 세계적으로 사용자 개인 정보 보호가 중요해지면서 사용자 정보를 다루는 시스템은 더 높은 수준의 보안 환경을 갖춰야 한다는 사회적 분위기가 형성됨에 따라 복잡해진 운영 환경을 간소화하면서 강화된 보안 규정을 만족할 수 있는 더 안전한 시스템과 업무 프로세스를 마련할 필요가 있었습니다.
이와 같은 여러 상황을 고려하면서 업무 효율성까지 극대화할 수 있는 방법을 찾기 위해 아래와 같이 두 가지 핵심 목표를 설정하고 운영 환경 개선 방법을 고민했습니다.
- 운영 중 발생하는 실수를 줄일 수 있도록 프 로세스 개선 및 자동화
- 운영 작업자에게 적절한 권한을 부여하고 작업 이력을 관리해 보안 수준 향상 및 사고 대비 강화
도입 목표 설정
LINE Pay는 2014년에 간편 결제 서비스를 출시한 이래 지속적으로 성장해 왔습니다. 일본과 대만, 태국 등 서비스 지역을 글로벌로 확장하고 교통 결제와 세금 납부, 게임 결제 등 다양한 결제 방식을 추가하면서 사용자가 지속적으로 증가해 왔는데요. 이에 따라 안정적으로 서비스를 제공하기 위해 인프라 시스템 또한 방대한 규모로 확장해야 했습니다.
이런 상황에서 기존 서버들을 효과적으로 오케스트레이션하고 새로운 서버를 동일한 환경으로 신속하게 투입할 수 있는 방안을 제공하기 위해 LINE Pay SRE 팀은 아래 네 가지 목표를 세웠습니다.
- 서버 구성 및 설정 코드화
- 프로세스 간소화
- 확장성
- 유연성 확보
서버 구성 및 설정 코드화
기존에는 서비스 실행 설정과 인프라 구성 코드를 주로 Git 기반으로 코드를 동기화하는 방식으로 관리했습니다. 그러나 서비스가 성장하면서 애플리케이션이 늘어나며 대규모 운영 환경을 구성하거나 원하는 서버에 직접 원격으로 명령을 실행할 수 있는 비정기적 임시 작업을 실행해야 하는 일이 잦아졌는데요. 이를 기존에 사용하던 도구로 작업하려니 기존 작업과의 경계가 모호해지고 실행 과정이 복잡해지면서 대응이 늦어지는 등 사고 발생 우려가 커졌습니다. 이를 해결하기 위해 하나의 환경에서 설정을 관리하고 조합할 수 있는 도구를 도입해서 한 명의 운영자가 대규모 인프라를 빠르고 일관성 있게 구성할 수 있는 IaC(Infrastructure as Code) 방 식을 도입하기로 결정했습니다.
프로세스 간소화
인프라 자동화 도구를 도입하면서 목표로 삼았던 것 중 하나는 아이러니하게도 완벽한 자동화가 아닌 수작업 간소화였습니다. 서버 설치나 도구 설치까지만 목표로 삼았다면 완전 자동화도 충분히 가능했을 텐데요. 사실 서비스를 운영하면서 가장 많은 시간을 잡아먹는 작업은 서버 설치나 설정 동기화 작업이 아니었습니다. 로그 추출이나 덤프 작업, 파일 확인, ACL(access control list) 확인, 이슈 확인을 위한 모니터링 정보 수집 등 간헐적인 이슈에 대응하기 위해 특정 서버에서 명령어를 직접 실행하는 비정기적 임시 작업(이하 애드혹(ad-hoc) 작업)이었습니다. 이에 이런 작업들의 요청 빈도를 월별로 수집해서 먼저 자동화해야 하는 작업 목록을 선정한 뒤 개발자가 직접 실행할 수 있는 도구를 제공해 프로세스를 개선하고자 했습니다.
확장성
LINE Pay 서비스 내부에는 모니터링을 비롯해 통합 테스트 환경과 CI/CD 도구 등 다양한 비기능적 플랫폼들이 있습니다. 저희는 운영 자동화 도구를 도입하면서 단순히 작업을 자동화하는 도구에 그치지 않고 기존 솔루션들과 연동해 시너지를 발휘할 수 있는 도구로 자리 잡기를 바랐습니다. 예를 들어 기존 서비스에서 간단히 웹훅을 호출하는 것만으로도 모니터링 시스템에서 장애를 감지했을 때 알림을 발송하면서 동시에 대상 시스템의 상태를 확인하는 작업을 실행하는 세미 오토 힐링(semi auto-healing) 기능이 작동하거나 성능 테스트 후 리포트가 생성되는 등 활용 가치를 높일 수 있는 방향으로 계획했습니다.
유연성
자동화 대상에 새로 구성하는 인프라뿐 아니라 기 존 환경도 포함됐습니다. 따라서 모든 환경에 호환되고 별도 변경 작업이 필요하지 않는 자동화 구성 방법을 지원하는 도구를 찾았습니다.
자동화 도구 선택
저희는 LINE Pay 시스템 환경에서 앞서 말씀드린 네 가지 목표를 달성할 수 있는 도구가 무엇인지 검토했습니다. 운영 자동화를 진행하는 데 사용할 수 있는 도구는 매우 다양한데요. 여러 도구 중 개발자 사이에서 인기가 높아 많이 사용하고 있는 Terraform과 SaltStack, Ansible로 선택 범위를 좁혀서 검토를 시작했습니다.
자동화 도구 검토 - Terraform vs SaltStack vs Ansible
Terraform | Ansible | SaltStack | |
---|---|---|---|
지원하는 OS |
| 좌동 | 좌동 |
명령 푸시 | X | O | O |
실행 모듈 | 제공 환경별(물리 장비,클라우드 플랫폼 등) 빌트인 모듈을 제공하며, 제공 업체에서 Terraform에 모듈을 제공하기도 함(예: NCP Terraform) | 다양한 모듈 제공(약 3,300개) | 빌트인 모듈 제공(약 140개, 커뮤니티는 미흡) |
CLI 제공 | O | O | O |
용도 | 인프라 및 클라우드 서비스 프로비저닝 오케스트레이션(HashiCorp 전용 프로그래밍 언어로 다양한 클라우드 및 제공 환경 관리) | 설정 관리 도구(애플리케이션 설치 및 명령어 전달 등의 상세 작업에 적합) | 좌동 |
작업 방식 | 선언적(지속적으로 유지되기를 원하는 상태를 정의) | 절차적(정확한 작업 절차를 코드로 배치) | 좌동 |
GUI | X(기본 GUI는 없으며 타사에서 제공) | O(엔터프라이즈와 오픈소스 간 기능 차이 없음) | X(제공하지만 기능 미약) |
라이선스 | 오픈소스, 엔터프라이즈 | 좌동 | 좌동(오픈소스와 엔터프라이즈 간 기능 차이 없음) |
장점 | 선언된 환경을 자동으로 계산하고 복구하기 때문에 일정하게 변하지 않는 상태가 필요한 환경에 적합 | SSH 기반 통신이며, 별도 에이전트가 필요하지 않고, 가장 널리 사용하는 설정 관리 도구 | 파일 전송용 명령어 salt-cp 등 애드혹 작업 편의를 제공하며, zeroMQ 기반의 발행/구독 통신으로 대규모 설정 관리에 용이하고, 사용자 정책 제공 |
단점 | 작업의 전체 모델링을 선언적 모델로 처리할 수는 없음(소프트웨어 설치, 패치, 네트워크 액세스 권한 변경 등) | 버전에 따라 Python 버전이 낮은 타깃 서버는 관리할 수 없으며, 각 타깃마다 SSH로 연결하므로 대규모 배포에는 적합하지 않음 | 에이전트 기반으로 타깃 서버에 '미니언(minion)'이라는 에이전트를 설치해야 하며 커뮤니티가 상대적으로 미약함 |
Terraform
프로비저닝 도구인 Terraform은 선언형 접근 방식으로 서버를 생성 단계부터 프로비저닝하고 오케스트레이션할 수 있다는 점이 매력적이었습니다. 특히 원하는 상태를 'state'라는 파일로 저장해 갖고 있다면 언제든지 현재 상태와 원하는 상태를 비교해 원하는 상태로 동기화할 수 있는데요. 이를 통해 간단하게 확장하거나 축소할 수 있고, 전반적인 인프라를 구성할 수 있다는 장점이 있었습니다.
만약 CSP(Cloud Service Provider)를 자유롭게 선택할 수 있는 환경이었다면 이와 같은 장점이 확실한 도입 이유가 될 수 있었겠지만, LINE Pay는 보안이 중요한 금융 서비스의 특성 때문에 일반 클라우드 서비스 환경과는 다르게 내부적으로 보안이 강화된 네트워크와 서버 환경을 사용해야 한다는 등의 제약이 있어서 자유롭게 활용하기는 어려울 것 같다고 판단했습니다.
또한 배포와 확장은 이미 효율적인 프로세스와 대체재를 갖추고 있던 터라 Terraform은 제외하기로 결정했습니다. 프로비저닝보다는 서버가 제공된 상태에서 설정을 자동화하고 운영 특성 업무를 효율화하기에 좋은 인프라 관리 자동화 도구를 검토하는 게 낫다고 판단했습니다.
SaltStack
다음으로 검토한 도구는 SaltStack입니다. SaltStack은 마스터(master)/미니언(minion) 구조로 에이전트를 구성해 마스터에서 인증된 대상만 조작할 수 있으며, SaltStack의 ACL을 활용해 사용자에 따라 역할을 제한할 수 있습니다. 또한 ZeroMQ 기반으로 명령을 발행하고 구독하기 때문에 대규모 인프라를 관리하기에 적합한 도구입니다.
다만 실제 사용할 때 개발자가 직접 실행할 수 있도록 인터페이스를 제공할 필요가 있었는데요. 엔터프라이즈가 아닌 오픈소스 버전에서는 SaltStack UI가 공식적으로 제공되지 않았습니다. 또한 권한 관리 기능을 사용할 수 있게 UI를 개발하더라도 설정 자동화 도구의 설정 파일을 수정해 가면서 권한을 관리하는 것은 안전하지 않았고, 최초 설계 단계부터 권한 관리는 별도 시스템으로 분리하는 것으로 계획했기 때문에 큰 장점이 될 수 없었습니다.
결정적으로 SaltStack을 선택하지 않은 이유는 대상 서버의 환경이 최신이 아니라 아주 오래된 환경인 경우에는 미니언을 설치하기 위해 새로운 패키지를 설치하는 것과 같은 변경 작업이 불가피했기 때문입니다. 이는 대상 서버를 변경하지 않는다는 목표에 부합하지 않았습니다. 설정 자동화 도구로 아주 좋은 선택지였지만, 관리자가 아닌 사용자의 입장에서 제공하고자 했던 저희 목표에는 조금 더 단순한 구조가 적합했습니다.
Ansible
Ansible은 2012년 출시돼 지속적으로 점유율이 증가하고 있으며, 어느 정도 안정성을 확보했고, 참고할 만한 사례도 많은 도구입니다. Ansible과 SaltStack은 사용 용도가 동일한데요. 표면적으로 드러나는 가장 큰 차이점은 통신 방식입니다. SaltStack은 에이전트 방식으로 마스터에서 인증된 대상만 조작하는 반면, Ansible은 SSH를 사용해 원격 호스트와 통신합니다.
Ansible은 널리 알려진 SSH 키 기반으로 서버에 접근해 설정 자동화를 구성할 수 있습니다. 물론 에이전트 기반 도구보다 통신 속도가 느리다는 단점은 있지만, SSH/WinRM 기반의 연결 방식은 다른 통식 방식보다 보안 수준이 높고 별도 에이전트가 필요 없다는 장점이 있습니다. 또한 필요할 때에는 사용자가 개발한 모듈을 연동할 수 있다는 유연성을 갖췄고, Ansible 커뮤니티에 올라온 다양한 사용 사례를 참고할 수 있습니다. SaltStack처럼 권한을 제한할 수는 없지만, 앞서 말씀드린 바와 같이 권한은 외부 시스템으로 분리하는 것으로 계획했기 때문에 오히려 장점으로 작용했습니다.
웹 콘솔 검토 - AWX vs Rundeck
저희가 제공하려는 도구는 운영자뿐 아 니라 일반 개발자도 사용해야 하기 때문에 조금 더 사용자 친화적인 UI가 필요했고, 이를 위해 AWX와 Rundeck을 검토했습니다.
AWX
Ansible은 엔터프라이즈뿐 아니라 오픈소스 버전에서도 아래와 같은 AWX(Ansible Worker) UI를 제공합니다. AWX는 적용 사례가 많아서 사례를 토대로 빠르게 도입할 수 있다는 장점이 있으며, 내부 PoC를 거치면서 사용해 본 결과 권한 제한이나 인증 방식, 스케줄링 등 기능 관점에서 필요한 요구 조건을 모두 충족하고 있었습니다.
다만 Ansible을 학습하지 않는다면 이해하기 어렵거나 굳이 보이지 않아도 되는 항목들이 화면에 노출되고 있어서 사용 난이도가 조금 높다고 느껴졌습니다. 운영 작업에 익숙한 엔지니어라면 무리 없이 사용할 수 있는 수준으로 관리자에게는 유용한 도구가 될 수 있었지만, 그 외 일반 사용자에게는 조금 어려울 수 있다고 판단했는데요. 예를 들면 단순히 파일을 조회하거나 네트워크 ACL을 확인하고 싶은 사용자에게 Ansible의 개념을 이해해야 알아볼 수 있는 대상 서버의 Credential이나 Inventory, Playbook 등의 정보가 노출되는 것은 좋지 않다고 생각했습니다.
또한 필요한 기능을 Ansible이 아닌 Shell이나 Python 등의 스크립트로 간단하게 구현하거나 기존에 사용하고 있던 스크립트를 붙여 넣고 싶은 사례도 자주 발생할 것이라고 예상했기 때문에 특정 언어(Ansible)에 의존하는 도구가 아니라 조금 더 확장성 있는 도구를 검토하기로 결정했습니다.
Rundeck
Rundeck은 PagerDuty에서 공개한 오픈소스 자동화 도구로 작업을 스케쥴링하고 실행 및 관리할 수 있는 자동화 플랫폼입니다. 최근에 공개된 도구는 아니지만 설치가 간단하고 사용하기 편리해서 많은 운영자가 꾸준히 사용하고 있습니다.
오랜 시간 꾸준히 사용된 만큼 지속적으로 업데이트되고 있었고, 웹훅이나 스케줄링 같은 기본 기능뿐 아니라 HashiCorp의 Vault와 같은 다양한 플러그인과의 연동도 지원하고 있었기 때문에 충분히 검토해 볼 만한 도구라고 판단했습니다. 확인 결과 현재 지원하는 기능은 다음과 같았습니다.
카테고리 | 지원 여부 |
---|---|
UI 편의 | 라디오 버튼, 체크 박스, 선택 박스 등 간편한 입력 형식 생성 지원 |
실행 방법 | Ansible, 로컬, SSH, Stub 지원 |
인프라 리소스 관리 | Ansible Inventory, 스크립트, URL 등 인프라 리소스 관리 방식 지원 |
접근 제어 | YAML 포맷의 역할 기반 접근 제어 제공 및 그룹 단위 설정 |
로그인 방법 | 사전 인증(pre-authenticate), SSO, LDAP 지원 |
감사 | 실행 기록을 Rundeck DB와 로그로 각각 저장 |
그 외 | 웹훅 API, 스케줄러 기능 지원 |
도구 선택 결과 - Ansible + Rundeck
결론적으로 시스템 구성 관리를 위한 도구로는 Ansible을 선택했고, 워크플로를 정의하고 원격 서버를 선택해 Ansible이 정의된 워크플로를 실행시키기 위한 도구로는 Rundeck을 선택했습니다.
Rundeck의 가장 큰 장점은 UI를 직관적으로 구성할 수 있다는 점이었습니다. 처음 사용해 보는 사용자도 특별한 학습 없이 사용할 수 있도록 커스터마이징할 수 있기 때문입니다. 또한 Rundeck으로 채택하면서 확장성도 확보할 수 있었는데요. Rundeck은 작업을 실행할 때 Ansible로만 가능한 게 아니라 설정에 따라 기존의 운영자가 서버에서 사용하던 Shell이나 Python 형식의 운영 스크립트도 그대로 대시보드와 연결해 사용할 수 있었습니다. 이를 통해 마이그레이션 공수를 들이지 않고 편의성을 높일 수 있었습니다.
운영 작업 체계화 및 자동화
LINE Pay는 제공하는 서비스의 특성에 따라서 쿠버네티스 환경뿐 아니라 물리 서버나 가상 서버 환경 등 다양 한 환경을 함께 운영하고 있습니다. 최근 많이 사용하는 쿠버네티스는 컨테이너화한 애플리케이션을 배포하고 관리하는 플랫폼으로, 애플리케이션 구성을 컨테이너 이미지에 미리 정의해 관리할 수 있지만, 그렇지 않은 물리 서버나 가상 서버 환경에서는 운영자가 직접 처리해야 합니다.
예를 들어 가상 서버 환경에서 어떤 애플리케이션을 설치해야 한다면 운영자는 아래와 같은 단계를 거쳐서 애플리케이션을 설치하게 됩니다.
- 서버 환경에 로그인한다.
- 설치할 애플리케이션이 설치돼 있는지 확인한다.
- 설치가 필요한 경우 → 애플리케이션을 설치한다.
- 설치가 불필요한 경우 → 애플리케이션 버전 정보를 확인한다.
- 업데이트가 필요한 경우 → 애플리케이션을 업데이트한다.
- 종료
현업에서는 작업 내용에 따라 위 예시보다 훨씬 복잡한 흐름을 따라야 하는 경우도 많으며, 그동안의 경험에 비춰보면 많은 문제의 시작은 사람이기 때문에 아무리 간단한 작업이라고 하더라도 작업 중 실수가 발생할 가능성을 배제할 수는 없습니다. 또한 설사 작업 흐름이 복잡하더라도 관리할 서버가 10대 정도였다면 사람이 직접 수행하는 게 훨씬 더 효율적일 수도 있었을 텐데요. LINE Pay에서는 수천 대의 서버를 사용하고 있기 때문에 사람이 직접 관리한다는 것은 엄청난 시간 낭비가 될 수밖에 없었습니다.
저희 팀은 LINE Pay 개발자분들이 보다 가치 있는 활동에 집중하기를 바랐습니다. 이에 앞서 선정한 자 동화 도구를 적용해 운영 작업을 체계화하고 자동화하기 시작했는데요. 한 가지 문제를 발견했습니다.
LINE Pay 운영 환경에 Ansible을 그대로 적용했을 때 발생할 수 있는 문제
앞서 말씀드린 것처럼 몇 가지 자동화 도구를 비교한 끝에 Ansible이 가장 적합한 도구라고 판단해 선택했는데요. LINE Pay 서비스 환경에 Ansible에서 일반적으로 사용하는, 전체 인프라를 포괄적으로 설정하고 구성하는 방식을 그대로 적용하면 문제가 발생할 수 있다는 것을 발견했습니다. LINE Pay 서비스 환경은 서비스 특성에 따라 서버 구성이 달라질 수 있기 때문입니다.
LINE Pay 서비스는 아래와 같이 판매자와 구매자가 물건을 거래하는 과정에서 사용자와 가맹점 사이를 연결해 주고 모두가 더욱 편리하게 거래할 수 있도록 돕는 서비스입니다.
이와 같은 과정을 처리하기 위해 회원과 가맹점, 계좌, 결제, 정산, 이벤트, 할인 등의 여러 메인 비즈니스 도메인을 다루고 있고, 각 메인 비즈니스 도메인은 그 안에서 다시 다양한 하위 비즈니스 도메인으로 나뉩니다. 총 약 100개 정도의 하위 도메인이 존재하는데요. 각 하위 도메인마다 서버 그룹이 달라질 수 있습니다.
즉 LINE Pay 서비스를 관리하기 위해서는 수십 개의 서버 그룹에 설치된 애플리케이션 목록과 버전을 확인해 적절한 Ansible playbook을 선택해야 하는데 이런 상황에서 일반적으로 사용하는 포괄적으로 인프라를 설정하고 구성하는 방식을 사용하기는 어렵다고 판단했습니다.
서버 그룹별로 애플리케이션 버전을 관리해야 하는 문제 해결하기
결국 서버 그룹별로 애플리케이션 버전을 관리해야 하는 문제부터 해결해야 했습니다. 문제 해결 아이디어의 기본 개념은 서버 그룹에서 사용하는 애플리케이션과 설치 버전을 기록해 두고 Ansible playbook을 실행할 때마다 조회해서 활용하는 것입니다. 이를 구체화하고 체계화하기 위해 아래와 같은 구조로 서버 설정 정보 파일을 관리하고 사용하도록 구성했습니다.
- 서버 그룹 정보를 모아둘 신규 Git 리포지터리를 생성한다(이하 '서버 정보 Git').
- 서버 정보 Git에 서버 그룹 이름으로 YAML 파일을 생성한다(이하 '서버 정보 YAML 파일')
- 서버 그룹에 설치된 애플리케이션과 버전을 조사한 뒤 서버 정보 YAML 파일에 미리 정한 일정한 형식에 맞춰 기록한다.
- Ansible playbook을 실행할 때마다 서버 정보 YAML 파일을 동적으로 가져와 애플리케이션 버전 정보를 활용한다.
물론 Ansible playbook을 개발할 때 서버 정보 YAML 파일을 참고해 스크립트를 작성해야 하기 때문에 약간의 노력이 더 필요했지만, 더 이상 서버에 직접 접속해 확인할 필요가 없기에 LINE Pay 서비스 환경에서 반복적인 서버 작업을 줄이고 자동화할 수 있었습니다.
역할 기반 권한 분리
핀테크 서비스는 보안과 안정성이 특히 중요하기 때문에 늘 이를 더 향상시킬 수 있는 방안을 고민하고 있는데요. 그 일환으로 담당자의 역할에 따라 개발과 운영 환경의 접근 권한을 더욱 세밀하게 분리해 관리하기로 했습니다. 운영 환경은 접근하는 담당자를 최소화해서 보안 사고를 방지하고 긴급 상황 발생 시 처리 수행자를 명확하게 지정할 수 있게 만들고, 개발 환경은 개발자가 더욱 자유롭게 환경을 이용할 수 있도록 권한을 넓혀 서비스의 생산 속도를 높이는 것이 목적이었습니다.
개발과 운영 환경 분리 작업은 기능 관점에서 아래와 같이 세 가지로 요약할 수 있습니다.
- 담당자의 역할에 따라 작동할 수 있는 작업을 명확하게 구분한다.
- 담당자의 역할에 따라 열람할 수 있는 서버 목록을 구분한다.
- 감사(auditing) 활동에 필요한 정보를 제공하기 위해 작업 처리 이력을 일정 기관 보관한다.
위 조건을 모두 충족하기 위해 Rundeck의 ACL을 활용했습니다.
작업별 개발과 운영 역할 및 권한 분리하기
Rundeck ACL은 간편하면서 강력한 접근 제어 기능을 제공합니다. YAML 형태로 여러 규칙을 동시에 적용할 수 있기 때문에 관리자가 원하는 대부분의 요구 사항을 충족할 수 있습니다. 개발자와 운영자의 역할을 구분해야 했기에 기본적으로 팀의 역할에 따라 팀별로 접근 권한을 분리하되, 상황에 따라 필요한 인원에게 예외적인 접근 제어 정책을 부여하는 방식을 적용했습니다. 아래 권한 정의 예시를 살펴보겠습니다.
역할 이름 | 서버 조회 | 작업 조회 | 작업 실행 | RBAC 수정 권한 | 설명 |
---|---|---|---|---|---|
Dev1 Team | Dev1 Team에 할당된 서버만 조회 가능 | 모든 작업 조회 가능 | Dev1 Team에 할당된 작업만 실행 가능 | N | Dev1 Team에 할당된 서버를 조회할 수 있고, Dev1 Team 또는 전체에 허용된 작업을 Dev1 Team 서버에 실행할 수 있는 권한 |
Dev2 Team | Dev2 Team에 할당된 서버만 조회 가능 | 모든 작업 조회 가능 | Dev2 Team에 할당된 작업만 실행 가능 | N | Dev2 Team에 할당된 서버를 조회할 수 있고, Dev2 Team 또는 전체에 허용된 작업을 Dev2 Team 서버에 실행할 수 있는 권한 |
Admin | 모든 서버 조회 가능 | 모든 작업 조회 가능 | 모든 작업 실행 가능 | Y | 모든 서버를 조회할 수 있고, 모든 작업을 실행하거나 수정할 수 있는 권한 |
Super Admin | 모든 서버 조회 가능 | 모든 작업 조회 가능 | 모든 작업 실행 가능, 애드혹 | Y | Admin 권한에 Rundeck의 애드혹 기능까지 허용한 권한 |
Temp | 사전에 설정된 서버만 조회 가능 | 모든 작업 조회 가능 | 사전에 설정된 작업만 실행 가능 | N | 팀으로 분리하기 어렵거나 일시적으로 권한이 필요한 개발자에게 부여하는 권한 |
(RBAC: 역할 기반 접근 제어(role-based access control, RBAC))
위 예시에서 역할은 팀을 기반으로 그룹으로 나눈 것입니다. Dev1 Team, Dev2 Team 등 작동하는 서비스에 따라 역할 구분이 가능한 서버는 팀을 기반으로 역할 그룹을 만들어 해당 팀에서만 서버를 조회할 수 있도록 권한을 분리했습니다. 작업 실행 권한 또한 데이터 배치 작업이나 서버 동기화 작업 등 각 팀의 특성을 고려해 모두에게 실행 권한을 허용할 필요가 없는 작업은 팀 특성에 맞게 권한을 분리했는데요. 이때 파일 조회나 시스템 사용률, 네트워크 ACL 조회 등 서비스에 영향을 주지 않는 작업은 운영자에게 요청하지 않고 모든 개발자가 직접 실행할 수 있도록 전체에 권한을 허용했습니다.
Admin 역할에는 모든 서버 조회와 작업 실행 및 수정, RBAC 수정 권한을 부여했습니다. 딱 한 가지 권한만 제한했는데요. Rundeck에서 제공하는 원격 명령 기능입니다. 매우 편리한 기능이지만 템플릿화하지 않은 임의 명령어는 예측하기 어려운 상황을 발생시킬 수 있기 때문에 반드시 필요한 경우에만 사용할 수 있도록 Super Admin 역할에만 기능 사용 권한을 부여했습니다.
이와 같이 Rundeck의 ACL은 YAML 포맷으로 대부분의 요구 상황을 정의해 낼 수 있을 정도로 자유도가 높습니다. 또한 아래와 같이 설정도 쉽게 진행할 수 있습니다.
먼저 Rundeck 메인 화면에서 Access Control을 선택합니다. 참고로 ACL을 설정하려면 Admin 이상의 권한을 보유하고 있는 사용자로 로그인해야 합니다.
ACL 페이지에서는 정의한 정책 개수와 전체 정보를 확인할 수 있으며, Create ACL Policy을 선택하면 새로운 ACL 정책을 정의할 수 있습니다.
상세 정책은 YAML 형식으로 정의할 수 있습니다.
사용자가 담당하는 서버에만 접근할 수 있도록 제한
LINE Pay에서는 서비스를 운영하기 위해 수천 대의 서버를 사용하고 있는데요. 누구나 모든 서버를 조회하고 작업을 실행할 수 있다면 보안 취약 지점이 될 수 있기 때문에 사용자의 역할에 따라 담당하는 서버만 조회하거나 실행할 수 있도록 제한하는 방법이 필요했습니다. Rundeck에서 강력한 그룹 기반 접근 제어 기능을 제공하기 때문에 간단한 조작만으로 충분히 해결할 수 있을 것으로 생각했지만, LINE Pay에서 관리하는 서비스와 서버가 너무 많다는 점이 문제였습니다.
Rundeck 구조를 아주 간단히 소개하자면, 노드 목록을 관리하는 'NODES'라는 탭이 있고, 대상 노드에 원하는 기능을 수행하는 'Node executor'라는 개념이 있습니다. Rundeck은 작업을 자동화하고 조직화해 실행하며 보안 수준을 높일 수 있는 오픈소스 플랫폼으로 다양한 플러그인을 활용해 작업 자동화를 수행하기 위한 외부 도구와 연동할 수 있는데요. 저희는 구성을 자동화하기 위해 노드 조회와 노드에서 원하는 기능을 수행하는 부분에 Ansible 기능을 연동해서 사용했습니다.
처음에는 Rundeck에서 노드를 조회할 때 Ansible에서 일반적으로 사용하는 정적 인벤토리(static inventory) 방식을 적용했습니다. hosts.ini라는 파일에 목적 호스트를 기록해 두는 방식인데요. LINE Pay는 운영하고 있는 서버가 너무 많아서 변경 발생 시 쉽게 대응할 수 없었고 무엇보다 이 방식은 자동화하기 어려웠기 때문에 동적 인벤토리(dynamic inventory) 방식으로 바꿨습니다.
동적 인벤토리 방식은 말 그대로 Ansible에서 사용할 호스트 목록을 동적으로 가져올 수 있는 기능으로 Python으로 스크립트를 작성하는 방식입니다. 정적 방식에서 동적 방식으로 전환하면 아래와 같은 네 가지 장점을 얻을 수 있습니다.
- 유연한 관리: 인벤토리 저장소를 자체 구현 시 호스트 추가나 삭제를 API로 손쉽게 할 수 있습니다.
- 인벤토리 자동화: Rundeck이나 Ansible의 저장소를 변경하지 않아도 자동으로 대상이 변경되기 때문에 관리하기 편합니다.
- 보안 강화: 호스트를 정적으로 특정 파일에 관리하는 게 아니라 별도 서비스에 저장하기 때문에 제한된 환경에서만 인벤토리 정보를 열람할 수 있습니다.
- 원활한 인벤토리 통합 관리 : 상황에 따라 AWS나 OpenStack, 쿠버네티스 등 다양한 서비스를 사용하는 대상을 관리해야 할 수 있는데요. Ansible은 다양한 서비스에 맞는 동적 인벤토리용 플러그인을 제공합니다.
다양한 호스트 정보를 규격화된 데이터로 저장하는 별도 동적 인벤토리 서비스를 구성해 LINE Pay에서 사용하는 서버 목록과 의미별 태그들을 함께 저장해 두고, Ansible에서는 hosts.ini 대신 Python 스크립트를 사용해 동적 인벤토리 서버에서 응답받은 정보를 YAML 기반으로 변환해 Rundeck 호스트 정보로 사용했습니다. 이때 관리하고 있는 담당자와 팀 정보를 함께 보관하면서 Rundeck에서는 여러 가지 속성으로 노출되도록 했으며, Rundeck의 접근 제어 기능을 활용해 로그인한 사용자가 관리하는 서버일 경우만 조회할 수 있도록 적용해 도입 목표를 달성할 수 있었습니다.
전체 구조를 간략하게 그림으로 나타내면 다음과 같습니다.
동적 인벤토리 서버는 Ansible 타깃이 되는 서버와 서버 메타데이터를 등록, 조회, 삭제, 업데이트할 수 있도록 API 서버로 구현했습니다. 운영자는 API를 통해 아래와 같이 서버 정보를 조작할 수 있습니다.
//서버 등록
POST localhost:8080/v1/server?componentName=admin
content-type: application/json
{
"hostName": "admin-server-01",
"env": "beta",
"tags": {
"component": "admin",
"team": "pay-dev"
},
"labels": {
"healthUrl": "http://foobar.linecorp.com/status"
}
}
//서버 조회
GET localhost:8080/v1/server?query=componentName:admin-server-01
//서버 삭제
DELETE localhost:8080/v1/server?hostName=admin-server-01
Ansible은 hosts.ini 대신 API로 서버 정보를 조회할 수 있도록 스크립트를 인벤토리에 저장합니다.
# 의사코드(pseudocode)로 작성
# see: https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html
def get_inventory_list(env):
# 환경 정보 검증
_check(env)
# 환경별 호스트 정보 조회
# API 응답 예제 [{"hostname": "linepay_host001", "ip": "0.0.0.0", info: {...}}]
host_list = _get_host_list(env)
# 응답 객체 생성
dict_data = {}
dict_data["_meta"] = {}
dict_data["_meta"]["hostvars"] = _convert_to_ansible_way(host_list)
return dict_data
위 예시대로 운영자가 동적 인벤토리 서버에 'admin' 컴포넌트를 등록했다면, Rundeck의 NODES 탭에서 아래 수식으로 검색했을 때 서버 정보가 출력됩니다.
tags: admin team: pay-dev
개발과 운영 환경을 분리하면서 부딪친 난관
도입부에서 말씀드린 것처럼 LINE Pay 서비스 환경을 자동화하는 개선 작업을 진행하게 된 가장 큰 이유는 보다 안전한 운영 환경을 만들기 위해 관리 방식을 개선하고 체계를 마련하기 위해서였습니다. 이를 위해 개발 환경과 운영 환경을 분리하면서 개발자가 서비스 운영 환경 서버에 접근하는 게 크게 제한됐는데요. 기존에 다양한 목적으로 개발자가 운영 환경에 접근하는 것을 허용하다가 이번 작업을 통해 개발자의 접근을 제한했고, 이에 인프라 전반에 대한 접근 권한을 가진 운영자가 운영 환경 서비스 작업을 위임받으면서 많은 시행착오와 난관을 겪을 수밖에 없었습니다.
작업을 위임받은 운영자는 아무래도 과거 서비스 변경 이력을 인지하기 어렵기 때문에 운영 환경에서 장애로 이어질 수 있는 포인트를 놓치기 쉬운데요. 이는 다양한 장애를 발생시킬 수 있는 요인이 될 수 있었습니다. 또한 Tomcat과 NGINX 등 미들웨어 설정을 변경하는 작업의 경우 운영 환경에 적용하기 전에 제대로 검증하는 것이 현실적으로 어려울 뿐 아니라, 가능하다고 하더라도 제약 사항이 많기 때문에 운영자가 장애를 발생시키지 않는다는 보장을 확보하고 모든 작업을 진행하는 것은 현실적으로 거의 불가능했습니다.
또한 궁극적으로 서비스의 신뢰도를 높이기 위해 개발과 운영 환경을 분리하는 작업을 진행했지만 비즈니스 관점의 이유로 복잡하고 많은 도메인을 한정된 인원이 맡아 운영하게 되는 경우도 있는데요. 이처럼 현실적으로 발생할 수 있는 한계를 파악하고 이를 극복할 수 있는 방안을 마련해야 비로소 안전하게 원래 목표를 달성할 수 있을 것이라고 판단했습니다. 이에 운영 작업의 신뢰도를 높일 수 있는 방안을 고민했습니다.
운영 작업 신 뢰도 높이기
거의 대부분의 엔지니어링 환경에서 개발뿐 아니라 테스트도 매우 중요한 영역으로 다루고 있습니다. 소프트웨어 산업에서도 이런 기조를 반영한 다양한 형태의 개발 방법론이 생겨날 정도로 테스트의 중요도는 증가하고 있는데요. 이에 맞춰 인프라 영역에서도 테스트와 자동화 등의 영역에서 다양한 시도를 하며 발전하고 있지만, 변경 리스크와 비용이 큰 인프라의 특성 때문에 보다 발전된 구조라도 적용하는 게 쉽지 않습니다.
특히 운영 환경에서는 긴급 상황이 발생하면 정상 서비스로의 복구를 최우선으로 삼기 때문에 근간을 개선하는 것보다는 그때그때 당장의 문제를 해결하고 보완하는 것이 주가 될 수밖에 없습니다. 개발 환경 또한 가볍게 기능 테스트가 가능한 수준을 제공하고 유지하는 것만으로도 충분하다고 여기는 경우가 많기 때문에 같은 비즈니스 도메인을 담당하더라도 인프라 구성은 대부분 운영과 개발 환경이 크게 다를 수밖에 없으며, 시간이 지날수록 그 차이가 점점 커지면서 어느 순간부터는 인프라 변경 사항을 개발 환경에서 테스트하는 행위가 무의미한 작업이 돼버립니다.
따라서 운영 환경과 개발 환경의 서버 인프라의 구성을 최대한 비슷하게 만들어 테스트를 진행할 수 있는 최소한의 신뢰성을 갖춘 환경을 확보하는 게 중요했습니다. 물론 이렇게 테스트를 거친 후에도 운영 환경에서 예상치 못한 문제가 발생할 수 있지만, 다양한 장애에 대응하면서 가장 중요한 것은 같은 장애가 다시 발생하는 것을 막는 것이며, 이때 같은 장애가 발생하지 않도록 만드는 것은 한 명의 엔지니어를 전문가로 양성시키는 것보다 누가 담당하더라도 항상 같은 결과를 보장할 수 있는 체 계화된 시스템을 마련하는 것이라고 판단했습니다.
이 목표를 달성하기 위해 항상 같은 작동과 결과를 보장할 수 있도록 모든 것이 정상일 때의 상태를 정확히 정의해서 표준화하는 과정이 필요했습니다. 이를 위해 설정 자동화 도구인 Ansible과, Ansible을 편리하게 사용할 수 있는 Rundeck을 도입해 아래와 같이 새로운 업무 방식을 정의하고 서버 작업 과정에서 같은 장애가 발생하지 않도록 체계화했습니다.
- Ansible 스크립트 개발
- 특정 서버 작업을 위해 실행할 Ansible 스크립트를 베타 환경에서 테스트
- Ansible 스크립트를 프로덕션 환경에 적용
- 성공 시 -> 종료
- 실패 시 -> 원인 분석
- 장애 유형에 따라 필요시 Ansible 스크립트 개선
운영 환경 적용 및 결과
LINE Pay에서는 Rundeck과 Ansible을 활용해서 아래와 같은 여러 자동화 작업을 만들었습니다.
- 다양한 애플리케이션 설치(JDK, Node, Tomcat, Nginx, OpenResty, Exporter)
- 애플리케이션 설정 갱신(Nginx 설정, Tomcat 설정, 덤프 추출)
- 애플리케이션 조작(덤프 추출, 서비스 서버 로드 밸런서 투입 여부 조작, 데몬 프로세스 조작)
- 기타 정보 처리(ACL 검사, Crontab 설정, 인증서 교체)
이를 통해 개발자는 운영 작업을 할 때 더 이상 개인 기억에 의존하지 않아도 됩니다. 코드화한 방식으로 이력과 운영 작업 흐름을 관리할 수 있고, 개인 경험에 상관없이 누구나 동일한 결과물이 나오도록 운영 작업을 진행할 수 있습니다.
이제 물리나 가상 환경에 존재하는 서버라고 하더라도 서버의 애플리케이션 설치나 구성 설정의 상태를 확인하기 위해 매번 로그인할 필요가 없습니다. 서버 정보 YAML 파일에 기록된 정보를 바탕으로 현재 상태를 확인하고 필요한 경우 파일 정보를 변경해서 범용적인 작업을 실행하는 것으로 안전하게 목표를 달성할 수 있습니다.
약 9개월 동안 운영 환경에 적용한 결과 아래와 같이 약 1,600건의 작업을 자동화로 대체할 수 있었습니다.
자동화 항목 | 담당자 | 수행 건수 |
---|---|---|
다양한 애플리케이션 설치 | 운영자 | 56 |
애플리케이션 설정 갱신 | 운영자 | 93 |
인증서 교체 | 운영자 | 51 |
서버 정보 조회 - 시스템 사용률 및 파일 조회 | 사용자 | 989 |
네트워크 정보 조회 - ACL 및 traceroute | 사용자 | 428 |
자동화를 통해 서버에 무엇을 설치하거나 간단한 작업을 확인하려고 직접 접근하는 리스크를 제거했을 뿐 아니라 수동으로 작업할 때 발생하는 소요 시간도 절약해 생산성을 높일 수 있었습니다.
또한 무엇보다도 이런 변경 작업을 모두 Git으로 관리하면서 변경 사항이 발생할 때마다 개발 팀과 운영 팀의 리뷰를 모두 통과해야만 운영 환경에 반영할 수 있는 더블 체크 과정을 넣었는데요. 이를 통해 운영 환경 인프라 작업을 보다 견고하게 만들었고, 장애가 발생하더라도 더 이상 개인의 몫이 아닌 모두의 책임이라는 문화를 만들어 낼 수 있었습니다. 이를 통해 효율성과 신뢰성을 유지하면서 건설적인 운영 환경을 마련할 수 있었습니다.
마치며
LINE Pay 서비스는 공개 후 약 10년 동안 폭발적인 성장을 경험했습니다. 다양한 서비스가 동시에 빠른 속도로 발전하다 보니 어느 순간 기능 성장과 더불어 양적인 성장을 뒷받침할 수 있는 고도화된 관리 기술이 필요하다는 것을 체감했습니다.
이에 다양한 목적으로 사용되는 복잡한 운영 리소스를 코드화하고 체계화해서 작업 간 안정성을 높였습니다. 또한 새로운 서버를 구성할 때 더 빠르게 운영 환경에 투입할 수 있도록 서버를 서비스 역할과 매칭해 정보만 정의하면 동일한 구성으로 설치할 수 있게 만들었고, 유연성도 확보했습니다. 운영자가 수동으로 진행하던 일련의 작업들을 Ansible을 사용해 코드로 구현했으며, Rundeck 대시보드에서 허용된 사용자에게만 작업 실행 권한을 제공하고 프로세스를 간소화할 수 있었습니다.
이 글이 DevOps 팀이나 더욱 안전하고 효율적으로 시스템을 운영할 수 있는 방법을 찾고 계신 개발자분들께 도움이 되기를 바라며 이만 마치겠습니다. 긴 글 읽어주셔서 감사합니다.