Container Orchestration/Kubernetes

Kubernetes Ingress에서 APK 파일의 Content-Type 올바르게 설정하기

Somaz 2026. 7. 2. 00:00
728x90
반응형

Overview

안드로이드 APK 파일을 웹서버를 통해 배포할 때, Content-Type이 올바르게 설정되지 않으면 다운로드나 설치에 문제가 발생할 수 있다.

 

특히 Kubernetes 환경에서 Nginx Ingress를 사용하는 경우, APK 파일이 `application/zip` 으로 잘못 인식되어 안드로이드 기기에서 제대로 설치되지 않는 문제를 경험할 수 있다.

이 글에서는 Kubernetes Nginx Ingress Controller를 사용하여 정적 파일 서버를 운영할 때, APK 파일의 Content-Type을 `application/vnd.android.package-archive` 로 올바르게 설정하는 방법을 다룬다.

 

 

 

Static File Server 관련해서는 아래의 글을 참고하면 된다.

2025.02.14 - [Container Orchestration/Kubernetes] - Kubernetes에 static-file-server 생성하기

 

Kubernetes에 static-file-server 생성하기

OverviewKubernetes에 간단하게 static-file-server 생성하는 방법에 대해서 알아본다. Static File Server 설치static-file-server Github 해당 Github 주소에 들어가서 내용을 읽어보면 Configuration에 Environment Variables, YAML

somaz.tistory.com

 

 

 

 

 

 

 

 


 

문제 상황

 

문제 발생

Static File Server를 통해 APK 파일을 배포하던 중, 다음과 같은 문제가 발생했다.

curl -I "http://file-server.somaz.link/apk/somaz_334.apk"

HTTP/1.1 200 OK
Date: Wed, 24 Dec 2025 03:04:41 GMT
Content-Type: application/zip  # 잘못된 Content-Type
Content-Length: 1499487247
Connection: keep-alive
Accept-Ranges: bytes
Access-Control-Allow-Headers: *
Access-Control-Allow-Origin: *
Cross-Origin-Resource-Policy: cross-origin
Last-Modified: Wed, 24 Dec 2025 02:38:35 GMT

 

 

 

문제점

  • APK 파일이 `application/zip` 으로 인식됨
  • 안드로이드 기기에서 다운로드 후 설치 시 문제 발생 가능
  • 일부 브라우저에서 APK를 ZIP 파일로 처리

 

 

올바른 Content Type

  • `Content-Type: application/vnd.android.package-archive`

 

 

 

환경 구성

 

 

사용 기술 스택

  • Kubernetes Cluster: On-premise
  • Ingress Controller: Nginx Ingress Controller (nginx-public-b)
  • Static File Server: halverneus/static-file-server:v1.8.11
  • Storage: NFS Server (Synology NAS)
  • LoadBalancer IP: 10.10.10.57

 

 

아키텍처

[Client] 
   ↓ HTTP Request
[Nginx Ingress Controller]
   ↓ LoadBalancer (10.10.10.57:32081)
[Static File Server Pod]
   ↓ ReadWriteMany
[NFS Storage (10.10.10.5:/volume1/nfs)]

 

 

 

 

 

 

 

해결 방법

 

 

1. Nginx Ingress Controller 설정

먼저 Nginx Ingress Controller에서 snippet annotations를 허용하도록 설정해야 한다.

 

 

 

`ingress-nginx-public-b-values.yaml`

global:
  image:
    registry: registry.k8s.io

controller:
  # Annotation 검증 활성화하되 snippet annotations 허용
  enableAnnotationValidations: true
  allowSnippetAnnotations: true
  
  # v1.12+ 버전에서는 annotation-risk-level 설정 필수
  config:
    annotations-risk-level: "Critical"
  
  ingressClassResource:
    name: nginx-public-b
    enabled: true
    default: false
    controllerValue: "k8s.io/ingress-nginx-public-b"
  
  service:
    enabled: true
    type: LoadBalancer
    loadBalancerIP: "10.10.10.57"

 

 

 

주요 설정 포인트

  1. `enableAnnotationValidations: true`
    • Annotation 검증을 활성화하여 보안 강화
  2. `allowSnippetAnnotations: true`
    • configuration-snippet 사용 허용
    • 커스텀 Nginx 설정 삽입 가능
  3. `annotations-risk-level: "Critical"`
    • Nginx Ingress v1.12 이상에서 필수
    • snippet annotations 사용을 위한 risk level 설정

 

 

 

 

2. Static File Server Helm Chart 설정

 

 

`values.yaml 전체 구성`

replicaCount: 2

image:
  repository: halverneus/static-file-server
  pullPolicy: IfNotPresent
  tag: "v1.8.11"

service:
  type: ClusterIP
  port: 80
  targetPort: 8080

ingress:
  enabled: true
  className: "nginx-public-b"
  annotations:
    nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
    nginx.ingress.kubernetes.io/ssl-passthrough: "false"
    nginx.ingress.kubernetes.io/rewrite-target: /
    # 핵심: APK 파일의 Content-Type 설정
    nginx.ingress.kubernetes.io/configuration-snippet: |
      if ($request_filename ~* \.apk$) {
        more_set_headers "Content-Type: application/vnd.android.package-archive";
      }
  hosts:
    - host: file-server.somaz.link
      paths:
        - path: /
          pathType: ImplementationSpecific
    - host: concrit.iptime.org
      paths:
        - path: /
          pathType: ImplementationSpecific

livenessProbe:
  httpGet:
    path: /
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10

envConfig:
  PORT: "8080"
  FOLDER: "/web"
  SHOW_LISTING: "true"
  ALLOW_INDEX: "true"
  CORS: "true"
  DEBUG: "true"

# NFS 스토리지 설정
persistentVolumeClaims:
  enabled: true
  items:
    - name: static-file-server-pvc
      accessModes:
        - ReadWriteMany
      storageClassName: nfs-client-nopath
      storage: 5Gi
      mountPath: /web
      selector:
        volumeName: static-file-server-pv

persistentVolumes:
  enabled: true
  items:
    - name: static-file-server-pv
      storage: 5Gi
      volumeMode: Filesystem
      accessModes:
        - ReadWriteMany
      reclaimPolicy: Retain
      storageClassName: nfs-client-nopath
      path: /volume1/nfs
      server: 10.10.10.5

 

 

 

3. 핵심 설정 상세 설명

 

 

configuration-snippet 분석

nginx.ingress.kubernetes.io/configuration-snippet: |
  if ($request_filename ~* \.apk$) {
    more_set_headers "Content-Type: application/vnd.android.package-archive";
  }

 

 

동작 원리

  1. 정규표현식 매칭: `$request_filename ~* \.apk$`
    • `~*`: 대소문자 구분 없는 정규표현식 매칭
    • `\.apk$`: `.apk` 로 끝나는 파일명
  2. 헤더 설정: `more_set_headers`
    • Nginx의 `headers-more` 모듈 사용
    • Content-Type 헤더를 강제로 덮어씀
  3. 조건부 적용:
    • APK 파일에만 Content-Type 변경
    • 다른 파일들은 기본 MIME 타입 유지

 

 

 

 

4. 결과 검증

curl -I "http://file-server.somaz.link/apk/somaz_343.apk"

HTTP/1.1 200 OK
Date: Fri, 02 Jan 2026 06:45:31 GMT
Content-Type: application/vnd.android.package-archive  # 올바른 Content-Type
Content-Length: 1427609073
Connection: keep-alive
Accept-Ranges: bytes
Access-Control-Allow-Headers: *
Access-Control-Allow-Origin: *
Cross-Origin-Resource-Policy: cross-origin
Last-Modified: Thu, 01 Jan 2026 15:56:22 GMT

 

 

성공! `Content-Type` 이 `application/vnd.android.package-archive` 로 올바르게 설정되었다.

 

 

 

 

 


 

 

 

 

 

트러블슈팅

 

 

문제 1: snippet annotations이 적용되지 않음

 

 

증상

# Ingress 이벤트 확인
kubectl describe ingress static-file-server

Events:
  Type     Reason  Age   From                      Message
  ----     ------  ----  ----                      -------
  Warning  Rejected  1m   nginx-ingress-controller  Snippet annotations are not allowed

 

 

원인

  • Ingress Controller에서 `allowSnippetAnnotations: false` 설정
  • 또는 annotations-risk-level 미설정 (v1.12+)

 

 

해결

# Ingress Controller values.yaml 수정
controller:
  allowSnippetAnnotations: true
  config:
    annotations-risk-level: "Critical"

# 재배포
helm upgrade nginx-public-b ingress-nginx/ingress-nginx \
  -f ingress-nginx-public-b-values.yaml \
  -n ingress-nginx

 

 

 

문제 2: Content-Type이 여전히 변경되지 않음

 

 

디버깅 단계

# 1. Ingress Annotation 확인
kubectl get ingress static-file-server -o yaml | grep -A 5 "configuration-snippet"

# 2. Nginx Pod에서 설정 확인
kubectl exec -n ingress-nginx <nginx-pod> -- cat /etc/nginx/nginx.conf

# 3. Nginx 로그 확인
kubectl logs -n ingress-nginx <nginx-pod> --tail=100

# 4. 캐시 클리어 및 재시작
kubectl rollout restart deployment -n ingress-nginx

 

 

체크리스트

  • `allowSnippetAnnotations: true` 설정됨
  • `annotations-risk-level: "Critical"` 설정됨 (v1.12+)
  • Ingress의 ingressClassName이 올바름
  • Nginx Controller 재시작됨

 

 

 

문제 3: 특정 경로에서만 적용 안됨

 

 

원인

  • Path 설정 문제
  • 정규표현식 매칭 실패

 

 

 

해결

# 정규표현식 테스트
nginx.ingress.kubernetes.io/configuration-snippet: |
  # 디버그 로그 추가
  if ($request_filename ~* \.apk$) {
    more_set_headers "X-Debug: APK-File-Detected";
    more_set_headers "Content-Type: application/vnd.android.package-archive";
  }

 

 

 

테스트

curl -I "http://your-domain/path/to/file.apk" | grep "X-Debug"
# X-Debug 헤더가 보이면 정규표현식이 동작하는 것

 

 

 

 

 

 

 

 

보안 고려사항

 

 

snippet annotations 사용 시 주의사항

 

 

리스크

  • Nginx 설정에 임의의 코드 삽입 가능
  • 잘못된 설정 시 전체 Ingress 장애 발생 가능
  • 보안 취약점 노출 가능성

 

 

권장 사항

  1. 최소 권한 원칙
    • snippet annotations는 꼭 필요한 경우에만 사용
    • 가능하면 ConfigMap이나 server-snippet 사용
  2. 코드 검토
    • 모든 snippet 코드 리뷰 필수
    • 테스트 환경에서 충분히 검증 후 적용
  3. RBAC 설정
 
   # snippet 사용 권한 제한
   apiVersion: rbac.authorization.k8s.io/v1
   kind: Role
   metadata:
     name: ingress-snippet-admin
   rules:
   - apiGroups: ["networking.k8s.io"]
     resources: ["ingresses"]
     verbs: ["create", "update"]
     # snippet annotation 사용 가능한 사용자만
 
 
 
 
 

추가 최적화

 

 

1. 캐싱 설정

APK 파일은 크기가 크므로 적절한 캐싱 설정이 중요하다.

 
nginx.ingress.kubernetes.io/configuration-snippet: |
  if ($request_filename ~* \.apk$) {
    more_set_headers "Content-Type: application/vnd.android.package-archive";
    more_set_headers "Cache-Control: public, max-age=86400";
    more_set_headers "Expires: $date_gmt, 1d";
  }

 

 

2. 압축 비활성화

APK는 이미 압축되어 있으므로 추가 압축 불필요

nginx.ingress.kubernetes.io/configuration-snippet: |
  if ($request_filename ~* \.apk$) {
    more_set_headers "Content-Type: application/vnd.android.package-archive";
    gzip off;
  }

 

 

 

3. 대용량 파일 지원

controller:
  config:
    proxy-body-size: "2000m"  # APK 파일 크기에 맞게 조정
    proxy-buffer-size: "8k"
    proxy-buffers: "8 8k"

 

 

4. 다운로드 속도 제한

nginx.ingress.kubernetes.io/configuration-snippet: |
  if ($request_filename ~* \.apk$) {
    more_set_headers "Content-Type: application/vnd.android.package-archive";
    limit_rate 10m;  # 10MB/s로 제한
  }
 
 
 
 
 
 
 
 

 

 

 

마무리

Kubernetes 환경에서 APK 파일을 배포할 때 올바른 Content-Type 설정은 필수적이다. Nginx Ingress Controller의 configuration-snippet을 활용하면 파일 확장자별로 유연하게 MIME 타입을 제어할 수 있다.

 

 

핵심 요약

  1. Ingress Controller 설정: `allowSnippetAnnotations: true`, `annotations-risk-level: "Critical"`
  2. Ingress Annotation: configuration-snippet으로 정규표현식 매칭 및 헤더 설정
  3. 검증: curl -I로 Content-Type 확인
  4. 보안: snippet 사용 시 코드 리뷰 및 최소 권한 원칙 적용
  5. 최적화: 캐싱, 압축, 대용량 파일 지원 등 추가 설정

 

 

이 방법을 통해 안드로이드 앱 배포 서버의 사용자 경험을 크게 개선할 수 있으며, 특히 사내 앱 배포나 베타 테스트 환경에서 유용하게 활용할 수 있다.

 

 

주의사항

  • snippet annotations는 보안 리스크가 있으므로 신중하게 사용
  • 프로덕션 환경에 적용하기 전 충분한 테스트 필수
  • 정기적인 Nginx Ingress Controller 버전 업데이트로 보안 패치 적용
 
 
 
 
 

 

 

 

 


Reference

 

 

 

Somaz | DevOps Engineer | Kubernetes & Cloud Infrastructure Specialist

728x90
반응형