소셜 게임 백엔드 설정 예시

6291 단어 AWSRubyRails
이것은 Akatsuki Advent Calendar 2016의 첫날이다.

개시하다


붉은 팥에서 소셜 게임을 담당하는 백엔드, 서버 응용 프로그램, 그리고 기타 기능@the40san.
본고는 실제 개발/운용된 소셜 게임 인프라 구조와 루비온 레일스가 구축한 중대형 API 서버와 관련된 tips를 소개한다.

인프라 구성


아카데미의 소셜 게임은 백엔드에서 AWS를 사용했다.

구성 예(전체)


이것은 우리 회사 제품의 구성 예이다.전체적으로 이런 느낌이에요.

API 서버


게임 논리 등이 설치된 서버 애플리케이션이 실행되는 서버다.
WebAPI를 통해 액세스할 수 있습니다.
팥 중에서는 루비 온 레일스+유니콘+nginx의 구성이 대부분이다.
API 서버는 주로 EC2로 구성됩니다.
하나의 AZ가 떨어지지 않도록 서비스 전체가 떨어지기 위해서는 2개 이상의 AZ를 사용해야 한다.
API 서버는 외부에서 직접 액세스할 수 없으며 Security Group이 설정되어 있으며 ELB를 통해서만 액세스할 수 있습니다.
소셜게임에서는 제시간에 시작되는 이벤트와 순위전 막판 압박 등으로 짧은 시간에 급격한 부하가 발생해 API 서버가 언제든 대대적인 준비를 할 수 있다.
API 서버의 프로그램은 다음과 같습니다.
  • 필요한 소프트웨어를 설치한 제품 전용 AMI를 Capistrano로 디버깅하는 방법

  • ECSECR를 이용하여 docker로 디버깅하는 방법
  • 기다린다ECS를 사용할 때 AutoScaling를 동시에 사용하여 서버 대수를 간단하게 증가하거나 줄일 수 있다.

    배치 서버


    일괄 처리가 필요할 때만 존재합니다.
    API 서버와 마찬가지로 EC2로 구성됩니다.
    API 서버와 달리 외부 요청을 받지 않고 배치 처리에 필요한 프로그램만 실행합니다.

    데이터베이스


    게임 사용자 데이터를 관리하는 서버입니다.
    데이터베이스 이용RDS for MySQL을 천천히 활용했다.
    병목이 될 때 가장 역할을 발휘할 수 있는 것은 데이터베이스다.
    모든 데이터베이스 실례의 성능에 한계가 있기 때문에 피크값의 부하를 예측할 수 없다면 규모화된 구조를 채택해야 한다.
    학원 데이터베이스의 구조는 다음과 같은 두 가지 유형이 있다.

    Master-Slave


    MySQL 애플리케이션을 이용하여 여러 개의 슬레이브를 준비하고 가능한 한 슬레이브로 읽는 구조를 분산시킨다.
    쓰기와 쓰기 후의 데이터는 마스터에서만 얻을 수 있기 때문에 마스터는 병목에 빠지기 쉽다.

    수평 분할 + Slave


    데이터의 분할은 사용자의 고유한 ID와 Salo gate 키 등을 통해 여러 인스턴스에 할당됩니다.
    수평 분할 시 서비스 시작 시 16~32 부분을 미리 분할한다.
    서비스 규모가 작거나 서비스를 시작할 때 여러 개의 데이터 가상을 하나의 서버CREATE DATABASE로 분할하고 축소율이 필요할 때 데이터를 다른 실례로 이동한다.
    슬레이브는 비상시 승격과 리드 분산을 위해 구축되는 경우가 있다.
    앞으로 데이터베이스를 설계할 때 주저하지 않고 이곳을 선택할 수 있다.
    그러나 일부는 수직으로 분할해야 하기 때문에 데이터베이스를 이용한 서버 응용의 설치 난이도도 높아진다.

    캐시


    내부 기억 장치의 데이터 저장.
    분산된 부하를 위해 데이터베이스 데이터의 일부분을 캐시하다
    Redis의 정렬 집합형 순위전을 활용한 데이터, 세션 정보 등 잃어버리기 쉬운 데이터를 저장한다.
    팥 이용ElastiCache은 리디스와 엠카치드를 활용한다.

    Redis


    레디스는 단식 레드이기 때문에 실례는 R3 시리즈(네이티브)를 사용해야 한다.
    이 밖에 도량은 1/コア数에서 감시해야 한다.(4개의 코어 중 CPU 사용률이 25%이면 100% 소진될 수 있습니다.)
    ElastiCache 한정된 이슈지만 리디스의 맥스클리어스는 65000까지(본가 상한선 unint32)만 설정될 수 있기 때문에 대규모 환경에서 리디스의db동거를 허용하지 않는 등 접속수에 유의해야 한다.
    액세스 빈도와 각 명령의 계산량을 확인하세요.무심코 고부하에 빠질 수도 있다.

    Memcached


    Memcached는 Redis에 비해 멀티스레드이기 때문에 여러 코어를 사용할 수 있습니다.
    Redis와 달리 데이터 구조를 저장할 수 없기 때문에 String으로 표현된 데이터나 Rails의 cachee를 사용할 수 있습니다스토어에 사용됩니다.
    조금 거칠게 처리해도 빠지지 않으니까 인출만 조심하면 되는 거죠?

    캐시 서버 비율


    캐시를 사용할 때 Consistent Hashing 비례를 고려합니다.
    "노드의 수를 늘릴 때 키를 최소한의 캐시로 이동시켜 저장한다"며 비례자 시 성능의 악화를 최소한으로 낮추자.
    한 키에 대량의 접근이 있으면 한 노드에 대한 부하가 집중되지만 이런 상황에서 키 이름에 접미사를 붙여 응용 측에서 부하를 분배하고 부하를 분산시킬 수 있다.
    최근에는 레디스 3.2 계열이 일라스티캐시에서 활용이 가능해 클러스터링을 통해 해결할 수 있다고 한다.

    스토리지 서비스


    게임 내부에 사용된 이미지와 사운드 등의 데이터를 저장하기 위한 서비스다.
    Amazon S3를 활용하고 있습니다.

    CDN(Contents Delivery Network)


    스토리지 서비스에서 사용자에게 데이터를 보내는 데 사용됩니다.
    팥 사용CloudFront.
    사용자의 물리적 위치에 대해 가장 좋은 위치에서 데이터를 다운로드할 수 있어 더욱 빨리 데이터를 다운로드할 수 있다.
    일본에서는 거의 시동이 걸렸기 때문에 해외발 대규모 방문을 예상하지 못했고, 요금류는 200개를 사용했다.

    로드 밸런서


    이는 API 서버에 대한 액세스를 분산하기 위한 것입니다.
    적성은 Elastic Load Balancer(Classic Load Balancer)를 사용합니다.

    Rails에 대규모 API 서버를 쓰기 위해서...


    루비 버전은 최대한 최신 버전으로.


    루비는 버전 업그레이드마다 성능 조정이 이뤄지기 때문에 새로운 버전을 활용하면 성능이 향상된다.가능한 최신 것을 사용하세요.

    연기가 잘 어울리다.


    성능 조정은 서버 비용을 삭감하기 위해 안정적인 서비스를 위해 순서대로 진행해야 한다.
    성능 조정의 기본은 병목 발견→해결이다.
    예를 들면 다음과 같습니다.
  • 루비프로프를 이용하여 코드 자체의 실행 속도를 수정
  • preload 등 eager loading을 이용하여 N+1을 수정하는 조회
  • 일반 조회 로그log, slow log 등을 분석하여 조회 자체의 조화와 횟수를 최적화
  • 캐시 서버에 데이터 배치
  • etc..
  • Uniccorn 재부팅 후 DB 드랍 상황


    현상.


    디버그 시 Uniccornkill -USR2을 다시 시작하면 대량SHOW FULL FIELDS table_name이 DB로 날아간다.
    소규모는 참을 수 있지만 대규모는 DB가 떨어지고 끌려가면 API 서버가 떨어진다(대고장)
    일정 규모의 Rails 애플리케이션이라면 반드시 통과할 것입니다.

    까닭


    ActiveRecord는 로드할 때 모드 정보를 가져와야 하기 때문에 실행SHOW FULL FIELDS table_name합니다.
    Uniccorn의 작업 프로그램 수입니다.×읽는 모델 수만 발생했습니다.

    대책 방법


    진행rake db:schema:cache:dump, 사전 생성db/schema_cache.dump으로 해결할 수 있습니다.db/schema_cache.dump가 있는 경우 로드 시 DB에 릴리즈되지 않습니다SHOW FULL FIELDS table_name.
    자신의 이름ActiveRecord::Base#establish_connection이나 사용Octopus 또는Tako일 경우 아래 연결에 따라 캐시를 설정합니다.
    file_name = File.join(Rails.root, 'db', 'schema_cache.dump')
    ActiveRecord::Base.connection.schema_cache = Marshal.load(File.binread(filename))
    

    총결산


    간단한 백엔드 구조의 예와 Rails의 tips!

    좋은 웹페이지 즐겨찾기