> ## Documentation Index
> Fetch the complete documentation index at: https://ayakaleaf-pro.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# S3

> Kubernetes 배포에서 프로젝트 파일과 기록을 S3 호환 스토리지에 보관합니다.

프로젝트 파일과 그 기록은 **history-v1** 서비스가, 템플릿 파일은 **filestore** 서비스가 저장하며, 둘 다 모든 Ayakaleaf Pro 파드 안에 있습니다. 기본적으로 데이터 볼륨에 기록하며, S3를 사용하면 버킷에 기록합니다. 필요한 버킷과 권한은 [S3](https://ayakaleaf-pro.mintlify.site/ko/on-premises/configuration/overleaf-toolkit/s3.md)에 설명되어 있습니다.

| | |
| - | - |
| 서비스 | history-v1(프로젝트 파일 및 기록), filestore(템플릿 파일), 모든 Ayakaleaf Pro 파드에 포함 |
| 파드 → S3 | 파일을 읽거나 쓸 때마다 S3 엔드포인트 |
| 필수 | [여러 레플리카](https://ayakaleaf-pro.mintlify.site/ko/on-premises/configuration/kubernetes/horizontal-scaling.md) 사용 시: 데이터 볼륨은 공유되지 않지만 버킷은 공유됨 |
| 주기적 작업 | 없음 |

> [!WARNING]
>
> 사용자가 프로젝트를 만들기 전에 S3를 선택하세요. 이미 볼륨에 파일을 보관하고 있는 인스턴스는 먼저 [S3 마이그레이션](https://ayakaleaf-pro.mintlify.site/ko/on-premises/maintenance/s3-migration.md)이 필요합니다.

### 구성

**1. 버킷 및 키**

[S3 설정](https://ayakaleaf-pro.mintlify.site/ko/on-premises/configuration/overleaf-toolkit/s3.md#s3-setup)에 설명된 대로 S3 서비스에서 세 개의 버킷 `overleaf-template-files`, `overleaf-project-blobs`, `overleaf-chunks`와 두 개의 제한된 사용자를 만들거나, 아래와 같이 클러스터에서 Garage를 실행하세요. 네 개의 키를 Secret에 넣으세요.

```bash wrap theme={null}
kubectl -n overleaf create secret generic overleaf-s3 \
  --from-literal=OVERLEAF_FILESTORE_S3_ACCESS_KEY_ID=<filestore key> \
  --from-literal=OVERLEAF_FILESTORE_S3_SECRET_ACCESS_KEY=<filestore secret> \
  --from-literal=OVERLEAF_HISTORY_S3_ACCESS_KEY_ID=<history key> \
  --from-literal=OVERLEAF_HISTORY_S3_SECRET_ACCESS_KEY=<history secret>
```

**2. ConfigMap**

```yaml title="30-config.yaml (additions)" wrap theme={null}
  OVERLEAF_FILESTORE_BACKEND: s3
  OVERLEAF_FILESTORE_TEMPLATE_FILES_BUCKET_NAME: overleaf-template-files
  OVERLEAF_FILESTORE_S3_REGION: eu-west-1
  OVERLEAF_HISTORY_BACKEND: s3
  OVERLEAF_HISTORY_PROJECT_BLOBS_BUCKET: overleaf-project-blobs
  OVERLEAF_HISTORY_CHUNKS_BUCKET: overleaf-chunks
  OVERLEAF_HISTORY_S3_REGION: eu-west-1
  # a self-hosted S3 service also needs its endpoint and path-style addressing
  # OVERLEAF_FILESTORE_S3_ENDPOINT: http://garage:3900
  # OVERLEAF_FILESTORE_S3_PATH_STYLE: "true"
  # OVERLEAF_HISTORY_S3_ENDPOINT: http://garage:3900
  # OVERLEAF_HISTORY_S3_PATH_STYLE: "true"
```

**3. 파드에 Secret 추가 및 재시작**

Deployment(또는 StatefulSet)에서 다른 Secret 옆에 이 Secret을 추가하세요.

```yaml theme={null}
          envFrom:
            - configMapRef: {name: overleaf-env}
            - secretRef: {name: overleaf-secrets}
            - secretRef: {name: overleaf-s3}
```

```bash theme={null}
kubectl apply -f 30-config.yaml -f 40-overleaf.yaml
kubectl -n overleaf rollout restart deployment/overleaf     # or statefulset/overleaf
```

### 클러스터 내 Garage

S3 서비스가 없다면 [Garage](https://garagehq.deuxfleurs.fr/)는 클러스터에서 실행되는 작은 S3 서버입니다. [수평 확장](https://ayakaleaf-pro.mintlify.site/ko/on-premises/configuration/kubernetes/horizontal-scaling.md) 구성은 Garage로 테스트되었습니다. 아래 명령은 키를 생성하고 Secret `overleaf-s3`를 직접 만들므로, 1단계의 Secret 명령 대신 이 명령을 사용하세요.

> [!NOTE]
>
> Overleaf 샘플에서 사용하는 MinIO는 더 이상 컨테이너 이미지를 게시하지 않습니다. `minio/minio`는 Docker Hub와 quay.io에서 사라졌습니다.

```yaml title="50-garage.yaml" wrap theme={null}
apiVersion: v1
kind: ConfigMap
metadata:
  name: garage-config
  namespace: overleaf
data:
  garage.toml: |
    metadata_dir = "/var/lib/garage/meta"
    data_dir = "/var/lib/garage/data"
    db_engine = "lmdb"
    replication_factor = 1
    rpc_bind_addr = "[::]:3901"
    rpc_public_addr = "127.0.0.1:3901"
    # rpc_secret and admin_token come from GARAGE_RPC_SECRET / GARAGE_ADMIN_TOKEN
    [s3_api]
    s3_region = "garage"          # Overleaf's OVERLEAF_*_S3_REGION must match
    api_bind_addr = "[::]:3900"
    [admin]
    api_bind_addr = "[::]:3903"
---
apiVersion: v1
kind: Service
metadata:
  name: garage
  namespace: overleaf
spec:
  selector:
    app: garage
  ports:
    - name: s3
      port: 3900
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: garage
  namespace: overleaf
spec:
  serviceName: garage
  replicas: 1
  selector:
    matchLabels:
      app: garage
  template:
    metadata:
      labels:
        app: garage
    spec:
      containers:
        - name: garage
          image: dxflrs/garage:v2.5.0
          env:
            - name: GARAGE_RPC_SECRET
              valueFrom: {secretKeyRef: {name: garage-secrets, key: rpc_secret}}
            - name: GARAGE_ADMIN_TOKEN
              valueFrom: {secretKeyRef: {name: garage-secrets, key: admin_token}}
          ports:
            - containerPort: 3900
          readinessProbe:
            tcpSocket:
              port: 3900
          resources:
            requests: {cpu: 50m, memory: 64Mi}
            limits: {memory: 512Mi}
          volumeMounts:
            - {name: config, mountPath: /etc/garage.toml, subPath: garage.toml}
            - {name: data, mountPath: /var/lib/garage}
      volumes:
        - name: config
          configMap:
            name: garage-config
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: [ReadWriteOnce]
        resources:
          requests:
            storage: 10Gi
```

시크릿을 생성하고 Garage를 시작한 다음, 레이아웃, 버킷, 두 개의 제한된 키를 만드세요. Garage 키 ID는 `GK`와 16진수 24자로, 시크릿은 16진수 64자로 구성됩니다.

```bash wrap theme={null}
kubectl -n overleaf create secret generic garage-secrets \
  --from-literal=rpc_secret="$(openssl rand -hex 32)" \
  --from-literal=admin_token="$(openssl rand -base64 32)"
kubectl -n overleaf create secret generic overleaf-s3 \
  --from-literal=OVERLEAF_FILESTORE_S3_ACCESS_KEY_ID="GK$(openssl rand -hex 12)" \
  --from-literal=OVERLEAF_FILESTORE_S3_SECRET_ACCESS_KEY="$(openssl rand -hex 32)" \
  --from-literal=OVERLEAF_HISTORY_S3_ACCESS_KEY_ID="GK$(openssl rand -hex 12)" \
  --from-literal=OVERLEAF_HISTORY_S3_SECRET_ACCESS_KEY="$(openssl rand -hex 32)"
kubectl apply -f 50-garage.yaml
kubectl -n overleaf rollout status statefulset/garage

g() { kubectl -n overleaf exec garage-0 -- /garage "$@"; }
s() { kubectl -n overleaf get secret overleaf-s3 -o jsonpath="{.data.$1}" | base64 -d; }
g layout assign -z dc1 -c 8G "$(g status 2>/dev/null | awk '/^[0-9a-f]{16}/{print $1; exit}')"
g layout apply --version 1
for b in overleaf-template-files overleaf-project-blobs overleaf-chunks; do g bucket create "$b"; done
g key import --yes -n overleaf-filestore "$(s OVERLEAF_FILESTORE_S3_ACCESS_KEY_ID)" "$(s OVERLEAF_FILESTORE_S3_SECRET_ACCESS_KEY)"
g key import --yes -n overleaf-history "$(s OVERLEAF_HISTORY_S3_ACCESS_KEY_ID)" "$(s OVERLEAF_HISTORY_S3_SECRET_ACCESS_KEY)"
g bucket allow --read --write overleaf-template-files --key overleaf-filestore
g bucket allow --read overleaf-project-blobs --key overleaf-filestore
g bucket allow --read --write overleaf-project-blobs --key overleaf-history
g bucket allow --read --write overleaf-chunks --key overleaf-history
```

권한은 [S3](https://ayakaleaf-pro.mintlify.site/ko/on-premises/configuration/overleaf-toolkit/s3.md#access-policies)의 정책과 일치합니다. Garage는 리전을 확인합니다. 여기서는 `garage`이며, `OVERLEAF_*_S3_REGION`도 같아야 합니다.

Garage를 사용하는 경우 [수평 확장](https://ayakaleaf-pro.mintlify.site/ko/on-premises/configuration/kubernetes/horizontal-scaling.md)의 `60-config.yaml`과 같이 ConfigMap에서 두 서비스 모두에 엔드포인트, `PATH_STYLE: "true"`, 리전 `garage`를 설정하세요.

### 여러 레플리카

모든 레플리카는 같은 버킷과 키를 사용해야 합니다. 버킷은 S3 제공업체의 도구로 백업하세요. 그러면 레플리카 자체 디스크에는 백업이 필요한 데이터가 남지 않습니다.

### Kubernetes에서의 테스트

레플리카 세 개와 위와 같은 Garage를 둔 Kubernetes 1.37에서, 프로젝트를 만들거나 파일을 업로드하거나 기록을 사용하거나 템플릿을 게시하는 이 페이지들의 모든 엔드 투 엔드 테스트가 통과했습니다. 이후 버킷 `overleaf-project-blobs`, `overleaf-chunks`, `overleaf-template-files`에는 객체가 저장되었고, 레플리카 자체의 `/var/lib/overleaf/data/history`는 비어 있었습니다.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.