티스토리 뷰

# network load balancer
helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.service.annotations."oci\.oraclecloud\.com/load-balancer-type"="nlb" \
  --set controller.service.annotations."oci-network-load-balancer\.oraclecloud\.com/security-list-management-mode"="All" \
  --set controller.service.loadBalancerIP="지정받은.IP주소를.적어.주세요" \
  --set controller.service.externalTrafficPolicy=Local

오라클 클라우드는 무료 티어를 정말 넉넉하게 줍니다. 후발주자인 만큼 홍보 목적으로 많이 주는 거겠지 싶지만서도, ARM A1 인스턴스가 4oCPU 와 24GB 램이 무료라니 말이 됩니까. 거기다가 한국 리전도 두 군데나 있습니다.

저는 마스토돈이라는 SNS(트위터 같은 마이크로블로깅)를 이용하고 있는데, 정확히는 워드프레스 같은 엔진의 개념에 가깝습니다. 어찌 되었든, 중요한 건 서버가 가끔 죽는다는 거죠. 마스토돈의 특성상 이메일처럼 동작하므로 다른 서버에 가서 "서버 죽었어~" 를 해도 되기는 하는데요, 기존에 쓰던 계정을 잠깐이나마 못 쓰게 된다는 건 아쉬운 일입니다.

이 덕분에 "안 죽는 서비스"에 대한 로망이 생기고, 쿠버네티스 공부에 손을 대게 되었습니다. (힘든 길 힘든 길...) 오랜 시간이 지난 결과 (대략 몇 개월?) 결과적으로 "쿠버네티스는 docker-compose 의 확장판이라고 생각하면 된다" 까지 오게 되었고, 이윽고 서버를 올리는 데 성공했습니다.

이게... 여러 가지 특수성이 겹쳐서 난이도가 꽤 올라갔었습니다. 그래서 그 과정을 정리해두고자 합니다. 전체 과정을 기록하진 않을거고, 트러블슈팅을 했던 내용만 기록하고자 합니다.

ARM64: 공식 차트를 쓸 수 없었던 이유

마스토돈 프로젝트에서는 helm 차트를 제공하고 있습니다. 다만, 마스토돈은 ruby on rails 프로젝트이고 sidekiq (job queue) 을 그대로 쓰고있어서 redis (sidekiq 백엔드) 가 추가로 필요하며, 메인 DB인 postgresql 도 필요로 합니다.

공식 마스토돈 helm 차트에서는 redis 와 postgreql 의 의존성 차트를 Bitnami 것을 걸어두고 있습니다. 이 분들의 이미지가 참 좋아보이는데 문제는 ARM64를 아직 지원하지 않는다는 것입니다. (글 쓰는 시점에서 그렇다는 거지, 나중에는 지원이 들어올 수도 있을 것입니다.)

왜 ARM이냐... 성능에 비해 가격이 엄청 싸다고 알고있어서입니다.

그래서 의존성이 걸려있는 두 helm 차트를 떼어내고, 차트 안에 공식 docker 이미지를 쓰는 postgresql 과 redis를 직접 넣기로 했습니다. 데이터를 영구히 유지해야 하는 앱에는 StatefulSet 이 적합하다고 하더라구요. pod과 PVC 등의 리소스 이름들이 일관된 형태로 유지되기 때문입니다. 그래서 일단 스케일링은 접어두고 단일 pod을 하나씩 띄우기로 했습니다.

이 삽질이 오래 걸렸는데, 두 가지 이유가 있었습니다.

  • k8s의 개념만 대충 훑어보고 돌입했다가 문서 보는 방법을 잘 몰라서 모르는 부분을 찾아보지 못했기 때문입니다.
  • helm 에서 쓰는 Go의 템플릿 문법을 여기에서 처음 봐서, 이게 뭐하는 건지 이해하는데 오래걸렸습니다.
    -> 셸에서 주로 쓰는 파이프 문법... 이라는 거만 인지하면 어렵지 않더라구요.

k8s 문서 잘 보기

k8s 의 문서는 크게 두 부분으로 이루어져있다는 점을 명심해야 쉽게 이해하고 모르는 부분을 하나하나 찾아볼 수 있습니다.

  • 개념 설명 파트
  • API 설명 파트 = YAML의 스키마 설명 파트

개념 설명이야 누구나 시작할 때 처음에 읽으실테고, 문제는 API 설명 파트입니다.

API 설명 파트가 YAML의 스키마를 설명하는 부분인데 그걸 몰랐습니다. k8s의 모든 리소스는 kubernetes API 서버를 경유하여 JSON 형태로 주고받는데, 우리가 쓰는 YAML은 그 API 서버에 저장되어 있는 (아마 etcd?) JSON 을 보기 편하게 만든 것에 불과하기 때문입니다.

여튼, YAML의 각 요소가 궁금하시면 API 설명 파트를 참조하세요. 레퍼런스 섹션에 있습니다.

오라클 클라우드 상에서의 구성

이제 클라우드 얘기를 해야겠습니다.

되도록이면 한국에 각종 서비스를 구성하고, 가급적이면 Oracle Cloud Infrastructure (aka OCI) 서비스를 이용하여 이용가격을 낮추고자 했습니다.

(여담인데, 집에서 쿠버네티스 환경을 구성하시려면 k3s + metalLB 를 설치하시거나 요즘에는 AWS에서 셀프 서비스 킷을 제공한다는 거 같더라구요. 그런 쪽을 이용하시면 될 거 같습니다.)

구성은 다음과 같습니다.

  • 외부 DNS: Cloudflare에 수동으로 구성
    • OCI의 DNS가 며칠이 지나도록 한국에서 갱신이 안 되더라구요. 포기했습니다.
  • 오브젝트 스토리지: OCI 의 오브젝트 스토리지를 사용했습니다.
    • 소규모 SNS 서비스의 대부분의 비용은 컴퓨팅과 오브젝트 스토리지에서 나가므로 적절한 안정성과 성능을 갖추면서도 싼 곳을 선택하는게 중요합니다.
      • wasabi 는 저번에 소셜망에서 여러 서버가 터져나간다는 소식을 들어서 배제했습니다.
      • storj DCS는 작은 파일을 자잘자잘하게 올리기에는 너무 느립니다.
      • AWS S3 괜찮긴 한데...
  • CDN: AWS Cloudfront를 사용했습니다.
    • CDN 서비스는 아직 아직 오라클에 없습니다.
    • Cloudflare는 Pay as you go 플랜이 없어서 최소 가격이 높아서 배제했습니다. 무료 플랜은 서울이나 대한민국이 아니죠.
    • 남는 건 AWS 등의 클라우드 벤더인데, 일본어로 된 OCI 오브젝트 스토리지 <-> AWS Cloudfront 연결 가이드가 있어서 Cloudfront를 선택했습니다. (계정이 이미 있기도 하구요)
  • K8S 서비스는 직접 설치하지 않는다면 오라클이 관리하는 OKE 외에는 선택지가 없습니다.
  • SMTP도 오라클 OCI 를 사용했는데 어떻게 설정했는지 잘 기억나지 않네요.

ElasticSearch 의 설치도 미뤘습니다.

OCI 오브젝트 스토리지의 S3 호환 API

도메인 방식으로는 버킷 접근이 안 되고 반드시 서브경로식으로 접근해야 한다는 건 많이들 아실테고요... 저는 이 점에 대해서는 기도 메타였습니다. (마스토돈이 지원해줘야 하니; 근데 다행히도 되던...)

S3 호환 API 키 찾기

S3 호환 오브젝트 스토리지 설정에서 가장 애 먹는 부분이 API 키를 찾는 것입니다. 그 이유는 오라클이 OCI의 해당 부분에서 "S3"라는 이름을 빼버렸기 때문입니다.

위치도 이상한 데에 있습니다. 오브젝트 스토리지나 버킷 섹션에 없고, 내 프로파일 -> 고객 암호 키(Customer Secret) 에 있습니다. 한글이니 더 못알아먹었습니다 (ㅋㅋㅋㅋ ㅠㅠ)

아, 마스토돈은 엔드포인트에 https 를 붙여줘야 합니다.

OCI 버킷과 AWS Cloudfront 연결하기

두세 가지 방식이 있습니다.

  • 버킷을 퍼블릭으로 만들기 - 제가 봤던 일본어 가이드에서 설명
  • API 키로 접근하게 만들기 - 번거로워 보이고 할 줄도 몰라서 시도조차 안 해봄 (추가 컴퓨팅 비용도 들 듯)
  • 사전 인증된 요청 URL 사용하기

처음에는 일본어 가이드를 따라서 버킷을 퍼블릭으로 만들었다가, 사전 인증된 요청 URL 방식으로 바꿨습니다. (비공개로 바꾸고요)

사전 인증된 요청이라는 방식은 좀 바보같아보이는 방법인데, 조회용 URL 자체가 비밀키가 되는 방법입니다. https니까 딱히 상관 없으려나? 거기다 날짜 제한을 반드시 걸어야 하는데, 오라클 포럼 어딘가에서도 그냥 먼 미래로 걸라고 하더라구요. 버킷 자체가 인터넷에 공개되는 거보다는 나으니까...

AWS Cloudfront 쪽에서는 해당 URL을 엔드포인트로 잡고 설정해두면 됩니다. Cloudfront 를 사용하는 경우 해당 주소의 SSL (TLS 인증서) 발급 및 자동 재발급도 무료이므로 AWS에서 SSL을 설정하시면 됩니다.

주소 자체가 비밀이긴 한데, 따라하시려면 어느 부분까지 넣어야 하는지는 알려드려야 해서 일부는 모자이크하지 않았습니다.

OCI 클라우드 셸

5GB 공간이 있는 웹콘솔을 무료로 줍니다. 계정이 남아있는 한 데이터가 삭제되지는 않는 것 같습니다.

곳곳에 OCI Cloud Shell 을 여는 버튼이 숨어있습니다. K8S 설정도 거기서 시키는 대로 하면 금방 가능합니다.

일단은 자동화된 CD를 구축하는 건 미뤄뒀으므로, 명령줄로 직접 helm install 을 치는 방향으로 진행했는데, 웹 환경의 셸을 이용했습니다.

안전하지 않을 수도 있겠지만, 일단 private 값들은 비공개 gitlab 저장소에 구성했습니다. 요즘엔 암호화해서 차트랑 같이 둔다던데 이미 이렇게 구성해놔서...

SMTP 서비스

전자메일 섹션에서 이런저런 번거로운 작업을 해주시면 설정이 됩니다. 사용하실 DNS 쪽과 같이 설정해주셔야 합니다.

  • 도메인 추가
  • DKIM 설정
  • SPF 설정
  • 승인된 발신자 설정 (승인된 발신자가 아니면 발신이 안 됨)
  • 그 외에는 밑의 mastodon 설정 yaml 에 적어놓은 걸 참고해주세요

오라클 SMTP 발신 서비스의 장점은 mailgun이나 sparkpost 같은 서비스와는 다르게 최소 비용이 없다는 거겠습니다. 대신 사용량이 좀 많아지면 오라클 쪽이 더 비싸지는 지점이 있더라구요. 취사선택하십셔

  smtp:
    auth_method: plain
    delivery_method: smtp
    enable_starttls_auto: true
    from_address: 이름 <전자메일_전송_@승인된_발신자>
    openssl_verify_mode: none
    login: "내 프로파일 > SMTP 인증서 > 사용자 이름"
    password: "내 프로파일 > SMTP 인증서 > 사용자 이름 (만들었을 때 한 번 나온 암호)"
    port: 587
    server: "전자메일 전송 > 구성 > 공용 끝점"
    tls: false

일단은 이런 설정으로 동작이 됐는데 맞는 설정인지는 모르겠습니다.

OKE K8S 클러스터 만들기

공개되는 템플릿쪽으로 만듭니다. 비공개면 클라우드 셸에서도 접속이 안 됐던걸로... 정확하게는 모릅니다.

A1 암페어 ARM 인스턴스 / 2oCPU 12GB램 2개 노드풀로 구성했습니다.

다만, 요금 문제가 있습니다. 어째 500원정도 나오길래 보니까 로드밸런서가 100MB 고정으로 만들어져있었습니다. 프리티어는 플렉서블 / 최소 10MB 최대 10MB 까지인데, 기본 템플릿이 거기에 맞게 고쳐지지 않았나 보네요. 시키는 대로 바꿔주시면 됩니다. 하단 추가

ingress-nginx 설치

외부 요청을 받아야 하니까 ingress-nginx 를 설치해야 합니다. ingress-nginx를 설치하면 LoadBalancer 타입의 Service도 같이 생기고, 거기에 맞는 로드밸런서(OCI 리소스)를 OCI 에서 자동으로 만들어주는데, 위에 취소선 그은 상황이 발생하지 않으려면 설치할 때 설정을 좀 해줘야 합니다.

일단 공용 IP를 예약합시다. 네트워킹 > IP 관리 > 예약된 공용 IP. 공용 IP를 예약해두지 않으면 자동으로 할당해주는데, 언제 바뀔지 누가 알겠습니까. Oracle의 IP 풀에서 하나를 빌리도록 합시다.

이제 helm 으로 설치하면 됩니다. 옵션은 읽어보시고 바꾸시되, loadBalancerIP 를 위에서 예약받은 IP로 지정해야 저기에 묶입니다. 그 외 옵션은 생성되는 로드밸런서가 flexible 이고 무료 제한인 10MB ~ 10MB 안에서 생성되게 하기 위한 것입니다.

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.service.annotations."service\.beta\.kubernetes\.io/oci-load-balancer-shape"="flexible" \
  --set controller.service.annotations."service\.beta\.kubernetes\.io/oci-load-balancer-shape-flex-min"=10  \
  --set controller.service.annotations."service\.beta\.kubernetes\.io/oci-load-balancer-shape-flex-max"=10 \
  --set controller.service.loadBalancerIP="지정받은.IP주소를.적어.주세요"

속도제한을 피하려면 L7/L4 로드밸런서가 아닌 L4/L3 네트워크 로드밸런서를 사용하시면 됩니다. 옵션은 문서를 읽어보시구요.

# network load balancer
helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.service.annotations."oci\.oraclecloud\.com/load-balancer-type"="nlb" \
  --set controller.service.annotations."oci-network-load-balancer\.oraclecloud\.com/security-list-management-mode"="All" \
  --set controller.service.loadBalancerIP="지정받은.IP주소를.적어.주세요" \
  --set controller.service.externalTrafficPolicy=Local

helm 으로 설치하지 않으면 나중에 업데이트 할 때 머리가 아프지 않을까 싶습니다. 저의 경우 업데이트가 안 되는 이유는 OCI 인터페이스에서 로드밸런서 타입을 아예 바꿔버린 거긴 했는데요... 가급적이면 설정은 설치할 때 패러미터로 넘겨주고 거기서 바꾸는 게 좋을 거 같습니다.

관련 문서

letsencrypt SSL 붙이기

cert-manager 를 사용합니다. 설치 yaml 이나 helm 등으로 구성요소들을 설치한 뒤, ClusterIssuer를 지정해서 넣으면 cert-manager 가 대응하는 ingress를 찾아서 알아서 인증서를 발급해줍니다.

아맞아 이거 하시려면 mastodon 차트 외에도 따로 nginx ingress 리소스도 이미 별도로 설치하셨어야 합니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt # 이 부분이 일치해야 함
  labels:
    이런저런 레이블들
  name: 리소스이름
spec:
  rules:
  - host: 도메인
    http:
      paths:
      - backend:
          service:
            name: 대상-서비스
            port:
              number: 대상-포트
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - 도메인
    secretName: 도메인-tls # 시크릿 이름
apiVersion: cert-manager.io/v1
kind: ClusterIssuer # Issuer 는 해당 namespace 전용입니다. 번거로우시죠? ClusterIssuer 가즈아
metadata:
  name: letsencrypt
spec:
  acme:
    # The ACME server URL
    server: https://acme-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: 적당히 이메일 적어주세요
    # Name of a secret used to store the ACME account private key
    # 이 이름의 secret 에 키가 들어간대요
    privateKeySecretRef:
      name: letsencrypt (ingress에 적어놓은 거랑 같은 거)
    # Enable the HTTP-01 challenge provider
    solvers:
    - http01:
        ingress:
          class: nginx

스토리지 리소스

postgresql 과 redis 의 영속성 저장공간을 확보해야 하니 스토리지를 어디에 마련할지도 중요하겠습니다.

별도로 스토리지 리소스를 만들고 싶지는 않은데, 자동 확장을 허용하고 싶으시다면 자동 확장이 기본으로 ON으로 되어있는 "oci-bv" 스토리지 클래스를 사용하시면 됩니다. "oci"가 더 낡은 포맷이라고 하더라구요.

다행히도 2개까지는 무료인 거 같아서...

관련 문서

https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengcreatingpersistentvolumeclaim.htm#contengcreatingpersistentvolumeclaim_topic_Provisioning_PVCs_on_BV_PV_Volume_expansion


이 외의 차트 관련해서 삽질한 건 굳이 설명 드릴 필요가 있나 싶구요 (하도 이리저리 삽질한 게 많아서...)

원본 차트가 AGPL 이었어서 공개해두긴 했습니다. https://gitlab.com/naru-cafe/don-chart 쓰시라고 공개해둔 것은 아니므로, bitnami 쪽에서 ARM64 이미지를 공개해줄 때까지 기다렸다가 그걸 이용하시거나 그냥 공식 차트로 x64 인스턴스를 올리시는 걸 추천드립니다.

아, `watch -n 3 -d kubectl get all` 유용합니다. OCI 탭 하나 더 띄우고 실행해놓으시면 됩니다. 근데 나중에 보니까 -w 옵션이 따로 있는 거 같더라구요. get all에는 그냥은 또 안 되는...

TODO

  • kubectl exec 같은 걸로 pod에 직접 접근이 되는지 확인 (이런 게 있나...?)
  • service account 가 뭔지 배우고 어떻게 설정되어있는지 보기
  • helm install 이나 helm upgrade --install 로 설치한다고 해서 자동으로 롤링 릴리즈를 해주는 게 아니더라구요.
  • CD 붙이기, elasticsearch 붙이기
  • auto scailing
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함