Docs Menu

Atlas 복원력 지침

MongoDB Atlas 인프라 중단, 시스템 유지 관리 등에 관계없이 가동 시간을 유지하도록 설계된 고성능 데이터베이스 입니다. 이 페이지의 지침 사용하여 애플리케이션 과 데이터베이스 의 복원력을 극대화하기 위한 설정을 계획하세요.

Atlas 클러스터는 최소 3개의 노드가 있는 복제본 세트 로 구성되며, 필요한 홀수 노드 수로 노드 수를 늘릴 수 있습니다. Atlas Atlas 애플리케이션 의 데이터를 프라이머리 노드 에 기록한 다음, 클러스터 내의 모든 세컨더리 노드에 해당 데이터를 점진적으로 복제하여 저장합니다. 데이터 저장 의 내구성 을 제어 하려면 특정 수의 보조 노드 가 쓰기 (write) 커밋한 후에만 쓰기 (write) 완료 하도록 애플리케이션 코드 의 쓰기 고려 (write concern) 조정할 수 있습니다 . 자세한 학습 은 읽기 및 쓰기 고려 구성을참조하세요.

기본값 으로 Atlas 선택한 cloud 제공자의 가용영역 중 하나 내의 가용영역에 클러스터 노드를 분산합니다. 예시 들어 클러스터 cloud 제공자 리전 us-east에 배포된 경우 Atlas 기본값 으로 us-east-a, us-east-bus-east-c 에 노드를 배포합니다.

고가용성 및 리전 간 노드 분산에 대해 자세히 학습 Atlas 고가용성을 위한 지침을 참조하세요.

노드 풀은 애플리케이션 직접 쓰고 읽는 프라이머리 노드 선택해야 하므로 Atlas 클러스터는 홀수 개의 노드로 구성되어야 합니다. 짝수 개의 노드로 구성된 클러스터 프라이머리 노드 선택되지 못하게 하는 교착 상태를 초래할 수 있습니다.

인프라 장애, 유지 관리 기간 또는 기타 이유로 프라이머리 노드를 사용할 수 없는 경우 Atlas 클러스터는 기존 세컨더리 노드를 프라이머리 노드의 역할로 승격시켜 데이터베이스 가용성을 유지하는 방식으로 자가 치유합니다. 이 프로세스에 대해 자세히 알아보려면 MongoDB Atlas가 고가용성을 제공하는 방법을 참조하세요.

Atlas는 예정된 유지 관리 동안 한 번에 하나의 노드에 롤링 방식으로 업데이트를 적용하여 가동 시간을 유지합니다. 이 과정에서 Atlas는 다른 비계획적인 프라이머리 노드 장애 시와 마찬가지로 필요 시 새로운 프라이머리 노드를 선출합니다.

유지 관리 기간을 구성할 때는 애플리케이션의 트래픽이 가장 적은 시간을 선택하세요.

Atlas 클러스터 성능, 쿼리 성능 등을 모니터 할 수 있는 내장 도구 를 제공합니다. 또한 Atlas 타사 서비스와 쉽게 통합됩니다.

클러스터를 적극적으로 모니터링 하면 쿼리 및 배포서버 성능에 대한 귀중한 인사이트를 얻을 수 있습니다. Atlas 의 모니터링 에 대해 자세히 학습 클러스터 모니터링모니터링 및 경고를 참조하세요.

재해 복구 워크플로가 필요한 다양한 시나리오를 시뮬레이션하여 이러한 이벤트에 대한 준비 상태를 측정할 수 있습니다. 특히 Atlas를 사용하면 프라이머리 노드 장애 조치를 테스트하고 지역 장애를 시뮬레이션할 수 있습니다. 애플리케이션을 프로덕션에 배포하기 전에 이러한 테스트를 실행할 것을 강력히 권장합니다.

종료 방지 기능을 활성화하여 Atlas 클러스터가 실수로 삭제되는 것을 방지할 수 있습니다. Terraform과 같은 IaC 도구를 활용하여 재배포로 인해 새 인프라가 프로비저닝되지 않도록 할 때는 종료 방지 기능을 활성화하는 것이 특히 중요합니다. 종료 방지 기능이 활성화된 클러스터 삭제 하려면 먼저 종료 방지 기능을 비활성화해야 합니다. 기본값 으로 Atlas 모든 클러스터에 대해 종료 방지 기능을 비활성화합니다.

Atlas 클라우드 백업은 클러스터 배포된 cloud 서비스 제공자 의 네이티브 스냅샷 기능을 사용하여 cloud 백업 저장 용이하게 합니다. 예시 들어, Amazon Web Services 에 클러스터 배포 경우, Amazon Web Services S3에서 구성 가능한 간격으로 생성된 스냅샷을 사용하여 클러스터의 데이터를 백업 하도록 선택할 수 있습니다.

데이터베이스 백업 및 스냅샷 검색에 대해 자세히 알아보려면 클러스터 백업을 참조하세요.

백업에 대한 권장 사항은 Atlas 백업 지침을 참조하세요.

클러스터 의 복원력을 향상하려면 클러스터 MongoDB 8.0으로 업그레이드 . MongoDB 8.0 은 회복 탄력성 과 관련된 다음과 같은 성능 개선 사항 및 새로운 기능을 도입합니다.

가능하면 애플리케이션의 프로그래밍 언어에 맞는 최신 운전자 버전 을 기반으로 구축된 연결 방법을 사용하는 것이 좋습니다. Atlas 제공하는 기본값 연결 문자열 도 좋은 출발점이지만, 특정 애플리케이션 및 배포서버 아키텍처의 컨텍스트에서 성능을 위해 이 문자열을 조정할 수도 있습니다.

예시 들어 로그인 역량 제공하는 microservices 의 경우 maxTimeMS 를 짧게 설정하다 , 애플리케이션 코드가 장기 실행 분석 작업 요청 인 경우 maxTimeMS 를 훨씬 더 큰 값으로 설정하다 수 있습니다. 클러스터.

연결 풀 설정을 조정하는 것 은 엔터프라이즈 수준의 애플리케이션 배포와 관련하여 특히 중요합니다.

데이터베이스 클라이언트 연결을 여는 것은 애플리케이션 Atlas cluster 에 쉽게 액세스 수 있도록 하는 클라이언트 연결 풀 유지 관리하는 데 있어 가장 리소스 집약적인 프로세스 중 하나입니다.

따라서 클라이언트 연결을 여는 이 프로세스 특정 애플리케이션 의 컨텍스트에서 어떻게, 언제 진행되기를 원하는지 생각해 볼 가치가 있습니다.

예시 들어, 사용자 수요를 충족하도록 Atlas cluster 확장 경우, 애플리케이션 에 지속적으로 필요한 최소 연결 풀 크기를 고려하여 애플리케이션 풀을 확장할 때 새 클라이언트 연결을 열 때 발생하는 추가 네트워킹 및 컴퓨팅 부하가 줄어들도록 합니다. 데이터베이스 작업을 늘려야 하는 애플리케이션의 시간에 민감한 요구를 훼손하지 않습니다.

minPoolSize 값과 maxPoolSize 값이 비슷하면 대부분의 데이터베이스 클라이언트 연결은 애플리케이션 스타트업 시 열립니다. 예시 들어 minPoolSize10 로 설정하다 되어 있고 maxPoolSize12로 설정하다 경우, 애플리케이션 스타트업 시 10 클라이언트 연결이 열리고 애플리케이션 런타임 중에 2 연결만 더 열 수 있습니다. . 그러나 minPoolSize10 로 설정하다 있고 maxPoolSize100로 설정하다 경우, 애플리케이션 런타임 중에 필요에 따라 최대 90 개의 추가 연결을 열 수 있습니다.

새 클라이언트 연결 열기와 관련된 추가 네트워크 오버헤드 . 따라서 애플리케이션 스타트업 시 해당 네트워크 비용 발생시킬지, 아니면 애플리케이션 런타임 중에 필요에 따라 동적으로 지연 시간 비용을 영향 시킬지 고려하세요. 한 번에 많은 수의 추가 연결을 열어야 하는 요청의 갑작스러운 급증

애플리케이션의 아키텍처는 이러한 고려 사항의 핵심입니다. 예시 , 애플리케이션 마이크로서비스로 배포 경우, 연결 풀 의 동적 확장 과 축소를 제어하는 수단으로 어떤 서비스가 Atlas 직접 호출해야 하는지 고려하세요. 또는 애플리케이션 배포서버 Amazon Web Services Lambda 와 같은 단일 스레드 리소스를 활용하는 경우 애플리케이션 하나의 클라이언트 연결만 열고 사용할 수 있으므로 minPoolSizemaxPoolSize 를 모두 로 설정하다 해야 합니다. 1.

대부분의 경우 애플리케이션의 워크로드별 쿼리는 Atlas에서 실행하는 데 걸리는 시간과 애플리케이션이 응답을 기다릴 수 있는 시간 측면에서 차이가 있을 것입니다.

Atlas에서 쿼리 타임아웃 동작을 전역적으로 설정할 수 있으며 쿼리 수준에서도 정의할 수도 있습니다.

Atlas 재시도 가능 읽기재시도 가능 쓰기 (write) 작업을 지원합니다. 활성화되면 Atlas 간헐적인 네트워크 중단에 대한 보호 조치로 읽기 및 쓰기 (write) 작업을 한 번 재시도합니다.

Atlas 클러스터는 결국 모든 노드에 모든 데이터를 복제합니다. 그러나 읽기 또는 쓰기 작업이 성공했다고 보고되기 전에 데이터가 복제되어야 하는 노드 수를 구성할 수 있습니다. Atlas에서는 읽기 고려쓰기 고려를 전역적으로 정의할 수 있으며 연결 문자열에서 클라이언트 수준으로도 정의할 수 있습니다. Atlas의 기본 쓰기 고려는 majority입니다. 이는 Atlas가 성공을 보고하기 전에 클러스터의 절반 이상의 노드에 데이터가 복제되어야 함을 의미합니다. 반대로 Atlas의 기본 읽기 고려는 local입니다. 이는 Atlas가 쿼리 시 클러스터의 한 노드에서만 데이터를 조회함을 의미합니다.

샤딩 을 사용하면 클러스터 를 수평으로 확장하다 할 수 있습니다. MongoDB 를 사용하면 일부 컬렉션을 샤드 하면서 동일한 클러스터 의 다른 컬렉션은 샤딩되지 않은 상태로 유지할 수 있습니다. 새 데이터베이스 를 생성하면 기본값 클러스터 에서 데이터 양이 가장 적은 샤드 가 해당 데이터베이스의 프라이머리 샤드 로 선택됩니다. 해당 데이터베이스 의 샤딩되지 않은 모든 컬렉션은 기본값 해당 프라이머리 샤드 에 있습니다. 이로 인해 워크로드가 증가함에 따라 프라이머리 샤드 에 대한 트래픽이 증가할 수 있으며, 특히 워크로드 워크로드 가 프라이머리 샤드 의 샤딩되지 않은 컬렉션에 집중되는 경우 더욱 그렇습니다.

이 워크로드 를 더 잘 분산하기 위해 MongoDB 8.0 에서는 moveCollection 명령을 사용하여 샤딩되지 않은 컬렉션 을 프라이머리 샤드 에서 다른 샤드로 이동할 수 있습니다. 이를 통해 예상 리소스 사용량이 적은 샤드에 활성이고 사용량이 많은 컬렉션을 배치할 수 있습니다. 이를 통해 다음을 수행할 수 있습니다.

  • 더 크고 복잡한 워크로드에서 성능을 최적화합니다.

  • 리소스 활용도를 높일 수 있습니다.

  • 샤드 전체에 날짜를 더 균등하게 배포합니다.

다음과 같은 상황에서는 컬렉션 을 격리하는 것이 좋습니다.

  • 처리량이 많은 여러 개의 비샤드형 컬렉션으로 인해 프라이머리 샤드 에 상당한 워크로드 가 발생하는 경우.

  • 샤딩되지 않은 컬렉션 이 다른 컬렉션의 병목 현상이 될 수 있는 향후 성장을 경험할 수 있을 것으로 예상합니다.

  • 클러스터당 하나의 컬렉션 배포서버 설계를 실행 우선 순위 또는 워크로드를 기준으로 해당 고객을 격리하려고 합니다.

  • 샤드에는 샤드되지 않은 컬렉션의 수로 인해 비례하는 양 이상의 데이터가 있습니다.

mongosh 를 사용하여 샤딩되지 않은 컬렉션 을 이동하는 방법을 학습 보려면 컬렉션 이동을 참조하세요 .

Atlas 재해 복구 모범 사례에 대한 권장 사항은 Atlas 재해 복구 지침고가용성 및 복구를 위한 권장 구성을 참조하세요.

예시 애플리케이션은 네트워크 장애 및 페일오버 이벤트에 대한 회복 탄력성을 보장하기 위해 다음 사항을 권장합니다.

  • 재시도 가능한 쓰기, 대다수 쓰기 고려 및 기본 읽기 고려와 함께 Atlas에서 제공하는 연결 문자열을 사용합니다.

  • maxTimeMS 메서드를 사용하여 optime 제한을 지정합니다. maxTimeMS 설정 방법에 대한 지침은 해당 드라이버 설명서를 참조하세요.

  • 키 중복 및 시간 초과에 대한 오류를 처리합니다.

이 애플리케이션은 클라이언트가 사용자 레코드를 만들거나 나열할 수 있는 HTTP API입니다. GET 및 POST 요청 http://localhost:3000을(를) 수락하는 엔드포인트를 노출합니다.

메서드
엔드포인트
설명

GET

/users

users 컬렉션에서 사용자 이름 목록을 가져옵니다.

POST

/users

요청 본문에 name이 필요합니다. users 컬렉션에 새 사용자를 추가합니다.

참고

다음 서버 애플리케이션 은 NanoHTTPD 를 사용합니다. 및 실행 하기 전에 프로젝트 JSON 에 종속성으로 추가해야 합니다.

1// File: App.java
2
3import java.util.Map;
4import java.util.logging.Logger;
5
6import org.bson.Document;
7import org.json.JSONArray;
8
9import com.mongodb.MongoException;
10import com.mongodb.client.MongoClient;
11import com.mongodb.client.MongoClients;
12import com.mongodb.client.MongoCollection;
13import com.mongodb.client.MongoDatabase;
14
15import fi.iki.elonen.NanoHTTPD;
16
17public class App extends NanoHTTPD {
18 private static final Logger LOGGER = Logger.getLogger(App.class.getName());
19
20 static int port = 3000;
21 static MongoClient client = null;
22
23 public App() throws Exception {
24 super(port);
25
26 // Replace the uri string with your MongoDB deployment's connection string
27 String uri = "<atlas-connection-string>";
28 client = MongoClients.create(uri);
29
30 start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
31 LOGGER.info("\nStarted the server: http://localhost:" + port + "/ \n");
32 }
33
34 public static void main(String[] args) {
35 try {
36 new App();
37 } catch (Exception e) {
38 LOGGER.severe("Couldn't start server:\n" + e);
39 }
40 }
41
42 @Override
43 public Response serve(IHTTPSession session) {
44 StringBuilder msg = new StringBuilder();
45 Map<String, String> params = session.getParms();
46
47 Method reqMethod = session.getMethod();
48 String uri = session.getUri();
49
50 if (Method.GET == reqMethod) {
51 if (uri.equals("/")) {
52 msg.append("Welcome to my API!");
53 } else if (uri.equals("/users")) {
54 msg.append(listUsers(client));
55 } else {
56 msg.append("Unrecognized URI: ").append(uri);
57 }
58 } else if (Method.POST == reqMethod) {
59 try {
60 String name = params.get("name");
61 if (name == null) {
62 throw new Exception("Unable to process POST request: 'name' parameter required");
63 } else {
64 insertUser(client, name);
65 msg.append("User successfully added!");
66 }
67 } catch (Exception e) {
68 msg.append(e);
69 }
70 }
71
72 return newFixedLengthResponse(msg.toString());
73 }
74
75 static String listUsers(MongoClient client) {
76 MongoDatabase database = client.getDatabase("test");
77 MongoCollection<Document> collection = database.getCollection("users");
78
79 final JSONArray jsonResults = new JSONArray();
80 collection.find().forEach((result) -> jsonResults.put(result.toJson()));
81
82 return jsonResults.toString();
83 }
84
85 static String insertUser(MongoClient client, String name) throws MongoException {
86 MongoDatabase database = client.getDatabase("test");
87 MongoCollection<Document> collection = database.getCollection("users");
88
89 collection.insertOne(new Document().append("name", name));
90 return "Successfully inserted user: " + name;
91 }
92}

참고

다음 서버 애플리케이션은 Express를 사용하며, 이를 실행하려면 프로젝트에 종속성으로 추가해야 합니다.

1const express = require('express');
2const bodyParser = require('body-parser');
3
4// Use the latest drivers by installing & importing them
5const MongoClient = require('mongodb').MongoClient;
6
7const app = express();
8app.use(bodyParser.json());
9app.use(bodyParser.urlencoded({ extended: true }));
10
11const uri = "mongodb+srv://<db_username>:<db_password>@cluster0-111xx.mongodb.net/test?retryWrites=true&w=majority";
12
13const client = new MongoClient(uri, {
14 useNewUrlParser: true,
15 useUnifiedTopology: true
16});
17
18// ----- API routes ----- //
19app.get('/', (req, res) => res.send('Welcome to my API!'));
20
21app.get('/users', (req, res) => {
22 const collection = client.db("test").collection("users");
23
24 collection
25 .find({})
26 .maxTimeMS(5000)
27 .toArray((err, data) => {
28 if (err) {
29 res.send("The request has timed out. Please check your connection and try again.");
30 }
31 return res.json(data);
32 });
33});
34
35app.post('/users', (req, res) => {
36 const collection = client.db("test").collection("users");
37 collection.insertOne({ name: req.body.name })
38 .then(result => {
39 res.send("User successfully added!");
40 }, err => {
41 res.send("An application error has occurred. Please try again.");
42 })
43});
44// ----- End of API routes ----- //
45
46app.listen(3000, () => {
47 console.log(`Listening on port 3000.`);
48 client.connect(err => {
49 if (err) {
50 console.log("Not connected: ", err);
51 process.exit(0);
52 }
53 console.log('Connected.');
54 });
55});

참고

다음 웹 애플리케이션 은 FastAPI 를 사용합니다. . 새 애플리케이션 을 만들려면 FastAPI 샘플 파일 을 사용합니다. 구조입니다.

1# File: main.py
2
3from fastapi import FastAPI, Body, Request, Response, HTTPException, status
4from fastapi.encoders import jsonable_encoder
5
6from typing import List
7from models import User
8
9import pymongo
10from pymongo import MongoClient
11from pymongo import errors
12
13# Replace the uri string with your |service| connection string
14uri = "<atlas-connection-string>"
15db = "test"
16
17app = FastAPI()
18
19@app.on_event("startup")
20def startup_db_client():
21 app.mongodb_client = MongoClient(uri)
22 app.database = app.mongodb_client[db]
23
24@app.on_event("shutdown")
25def shutdown_db_client():
26 app.mongodb_client.close()
27
28##### API ROUTES #####
29@app.get("/users", response_description="List all users", response_model=List[User])
30def list_users(request: Request):
31 try:
32 users = list(request.app.database["users"].find().max_time_ms(5000))
33 return users
34 except pymongo.errors.ExecutionTimeout:
35 raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="The request has timed out. Please check your connection and try again.")
36
37@app.post("/users", response_description="Create a new user", status_code=status.HTTP_201_CREATED)
38def new_user(request: Request, user: User = Body(...)):
39 user = jsonable_encoder(user)
40 try:
41 new_user = request.app.database["users"].insert_one(user)
42 return {"message":"User successfully added!"}
43 except pymongo.errors.DuplicateKeyError:
44 raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Could not create user due to existing '_id' value in the collection. Try again with a different '_id' value.")