만들리에/오락실 (스트리밍 게임)

추억의 오락실 (11/11) - 아저씨와 AWS/EKS

gamz 2021. 2. 14. 01:11

드디어 앱들을 배포할 시간이 되었다. 배포할 컴포넌트들은 게임 에뮬레이터와 인코딩을 담당하는 기판(Gipan)과 이를 WebRTC 혹은 유저 인터렉션으로 이어주는 오락기(Orakki), 그리고 이것들을 코디네이션하고 주요비즈로직을 담고 있으며 프론트의 API서버인 아줌마(Azumma), 프론트앱인 애새끼(Esekki, 이건 따로 안다뤘음)가 있다.

 

이번 포스팅에서는 이것들을 어디에 어떤식으로 배포할지를 다뤄보겠다. 물론 답은 정해져 있는데 어디에? AWS에 어떻게? EKS(k8s)를 이용해서 배포해보고자한다. 그리고 이 배포 관련 형상을 관리하는 프로젝트르의 메타포는 아저씨(Azussi)로 한다. 그 옛날 내가 다니던 오락실에서 난닝구 입은 아저씨가 오락실 문앞을 청소하시고 드라이버를 들고 조명이나 오락기를 뜯어 고치시던 모습이 떠올라서다.

 

배포(운영) 구조도

  • 일단 크게 Public 영역과 Private 영역이 있다. Public에는 인터넷 접근이 가능해야하는 ELB나 TURN서버가 실행되며 Private에는 k8s cluster 가 존재한다.

  • 유저(PC, 모바일 웹)는 ELB를 통해 사이트에 접근하고 API 서비스(Azumma)를 호출하며 게임 시그널링이 완료(WebRTC Session Established)된 후에는 TURN 서버를 통해 오락기와 통신한다.

  • k8s cluster 내에서는 오락실용 인스턴스 Pool (oraksil-pool)과 오락기용 인스턴스 Pool (orakki-pool)이 나눠져있다. 각 Pool(혹은 인스턴스 그룹)의 인스턴스 타입은 다르다.

  • ELB를 k8s Service에 직접 붙이는 방법도 있으나 그럼 서비스마다 ELB가 필요할 수도 있어서(비용 Up) Ingress(Nginx)를 두고 하나의 ELB로 들어오는 트래픽을 Ingress를 통해서 Service(Azumma or Esekki)로 라우팅한다.

 

클러스터 셋업 가이드 (Step by Step)

EKS 클러스터 구성

Console을 이용하여 EKS 클러스터 생성

여기서부터 시작하면 된다. 클러스터 이름을 입력하고 다음 단계로 가면

아래처럼 기본 설정부터 네트워크 설정, 로깅 설정을 하게 되는데 다 기본으로 설정했다. 

Step 1. 클러스터 기본 설정
Step 2. 네트워크 설정
Step 3. 로깅 설정
Step 4. 리뷰 및 생성

클러스터가 생성되는데는 5~10분 정도 소요된다. 다 생성되고 나면 기본 Pod들이 목록에 보인다.

 

CLI 툴(eksctl)을 이용하여 클러스터 생성

Console로도 충분히 구축 및 설정을 할 수 있지만 아무래도 코드레벨의 더 Declarative 한 방법으로 접근하는게 더 우아한 방법이다. 이를 도와주는 CLI 툴로 eksctl가 있다. AWS/EKS를 사용하는데 이 툴도 한몫하는 것 같다. k8s yaml 설정과 비슷하게 Cluster 구성 자체에 대한 상세한 설정을 yaml 파일로 담을 수 있다.

 

아래 오락실의 EKS 설정 예시가 있다. 정의한 것 이외의 설정들은 모두 eksctl가 기본 값으로 채워준다. 

# cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: oraksil-prod
  region: ap-northeast-2

# Pool(Instance Group) 마다 인스턴스 타입등을 다르게 설정
managedNodeGroups:
- name: pool-oraksil
  instanceType: t3.small
  desiredCapacity: 1
  volumeType: gp2
  volumeSize: 48
  ssh:
    allow: true
    publicKeyPath: ~/.ssh/id_rsa_eks.pub

- name: pool-orakki
  spot: true
  instanceType: c5a.large
  desiredCapacity: 1
  volumeType: gp2
  volumeSize: 32
  ssh:
    allow: true
    publicKeyPath: ~/.ssh/id_rsa_eks.pub

이걸 실행해보면, (AWS Key Id/Secret, SSH Keypair, AWS CLI 등의 설정은 여기서 다루지 않는다)

$ eksctl create cluster -f cluster.yaml
2021-02-24 23:14:16 [ℹ]  eksctl version 0.38.0
2021-02-24 23:14:16 [ℹ]  using region ap-northeast-2
2021-02-24 23:14:16 [ℹ]  setting availability zones to [ap-northeast-2a ap-northeast-2c ap-northeast-2d]
2021-02-24 23:14:16 [ℹ]  subnets for ap-northeast-2a - public:192.168.0.0/19 private:192.168.96.0/19
2021-02-24 23:14:16 [ℹ]  subnets for ap-northeast-2c - public:192.168.32.0/19 private:192.168.128.0/19
2021-02-24 23:14:16 [ℹ]  subnets for ap-northeast-2d - public:192.168.64.0/19 private:192.168.160.0/19
2021-02-24 23:14:16 [ℹ]  using SSH public key "/home/gonghee/.ssh/id_rsa_eks.pub" as "eksctl-oraksil-prod-nodegroup-pool-oraksil-5f:72:0f:8e:fd:8b:50:92:3f:68:91:83:e4:76:a2:6d"
2021-02-24 23:14:16 [ℹ]  using SSH public key "/home/gonghee/.ssh/id_rsa_eks.pub" as "eksctl-oraksil-prod-nodegroup-pool-orakki-5f:72:0f:8e:fd:8b:50:92:3f:68:91:83:e4:76:a2:6d"
2021-02-24 23:14:16 [ℹ]  using Kubernetes version 1.18
2021-02-24 23:14:16 [ℹ]  creating EKS cluster "oraksil-prod" in "ap-northeast-2" region with managed nodes     
2021-02-24 23:14:16 [ℹ]  2 nodegroups (pool-orakki, pool-oraksil) were included (based on the include/exclude rules)
2021-02-24 23:14:16 [ℹ]  will create a CloudFormation stack for cluster itself and 0 nodegroup stack(s)        
2021-02-24 23:14:16 [ℹ]  will create a CloudFormation stack for cluster itself and 2 managed nodegroup stack(s)2021-02-24 23:14:16 [ℹ]  if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=ap-northeast-2 --cluster=oraksil-prod'
2021-02-24 23:14:16 [ℹ]  CloudWatch logging will not be enabled for cluster "oraksil-prod" in "ap-northeast-2" 
2021-02-24 23:14:16 [ℹ]  you can enable it with 'eksctl utils update-cluster-logging --enable-types={SPECIFY-YOUR-LOG-TYPES-HERE (e.g. all)} --region=ap-northeast-2 --cluster=oraksil-prod'
2021-02-24 23:14:16 [ℹ]  Kubernetes API endpoint access will use default of {publicAccess=true, privateAccess=false} for cluster "oraksil-prod" in "ap-northeast-2"
2021-02-24 23:14:16 [ℹ]  2 sequential tasks: { create cluster control plane "oraksil-prod", 3 sequential sub-tasks: { wait for control plane to become ready, create addons, 2 parallel sub-tasks: { create managed nodegroup 
"pool-oraksil", create managed nodegroup "pool-orakki" } } }
2021-02-24 23:14:16 [ℹ]  building cluster stack "eksctl-oraksil-prod-cluster"
2021-02-24 23:14:17 [ℹ]  deploying stack "eksctl-oraksil-prod-cluster"
2021-02-24 23:14:17 [ℹ]  waiting for CloudFormation stack "eksctl-oraksil-prod-cluster"
2021-02-24 23:14:34 [ℹ]  waiting for CloudFormation stack "eksctl-oraksil-prod-cluster"
2021-02-24 23:14:53 [ℹ]  waiting for CloudFormation stack "eksctl-oraksil-prod-cluster"
...
2021-02-24 23:26:58 [ℹ]  waiting for CloudFormation stack "eksctl-oraksil-prod-cluster"
2021-02-24 23:27:17 [ℹ]  waiting for CloudFormation stack "eksctl-oraksil-prod-cluster"
2021-02-24 23:27:36 [ℹ]  waiting for CloudFormation stack "eksctl-oraksil-prod-cluster"
2021-02-24 23:27:38 [ℹ]  building managed nodegroup stack "eksctl-oraksil-prod-nodegroup-pool-orakki"
2021-02-24 23:27:38 [ℹ]  building managed nodegroup stack "eksctl-oraksil-prod-nodegroup-pool-oraksil"
2021-02-24 23:27:38 [ℹ]  deploying stack "eksctl-oraksil-prod-nodegroup-pool-orakki"
2021-02-24 23:27:38 [ℹ]  waiting for CloudFormation stack "eksctl-oraksil-prod-nodegroup-pool-orakki"
2021-02-24 23:27:38 [ℹ]  deploying stack "eksctl-oraksil-prod-nodegroup-pool-oraksil"
2021-02-24 23:27:38 [ℹ]  waiting for CloudFormation stack "eksctl-oraksil-prod-nodegroup-pool-oraksil"
2021-02-24 23:27:54 [ℹ]  waiting for CloudFormation stack "eksctl-oraksil-prod-nodegroup-pool-oraksil"
2021-02-24 23:27:58 [ℹ]  waiting for CloudFormation stack "eksctl-oraksil-prod-nodegroup-pool-orakki"
...
2021-02-24 23:32:29 [ℹ]  waiting for CloudFormation stack "eksctl-oraksil-prod-nodegroup-pool-orakki"
2021-02-24 23:32:49 [ℹ]  waiting for CloudFormation stack "eksctl-oraksil-prod-nodegroup-pool-orakki"
2021-02-24 23:32:50 [ℹ]  waiting for the control plane availability...
2021-02-24 23:32:50 [✔]  saved kubeconfig as "/home/gonghee/.kube/config"
2021-02-24 23:32:50 [ℹ]  no tasks
2021-02-24 23:32:50 [✔]  all EKS cluster resources for "oraksil-prod" have been created
2021-02-24 23:32:50 [ℹ]  nodegroup "pool-oraksil" has 1 node(s)
2021-02-24 23:32:50 [ℹ]  node "ip-192-168-63-120.ap-northeast-2.compute.internal" is ready
2021-02-24 23:32:50 [ℹ]  waiting for at least 1 node(s) to become ready in "pool-oraksil"
2021-02-24 23:32:50 [ℹ]  nodegroup "pool-oraksil" has 1 node(s)
2021-02-24 23:32:50 [ℹ]  node "ip-192-168-63-120.ap-northeast-2.compute.internal" is ready
2021-02-24 23:32:50 [ℹ]  nodegroup "pool-orakki" has 1 node(s)
2021-02-24 23:32:50 [ℹ]  node "ip-192-168-51-210.ap-northeast-2.compute.internal" is ready
2021-02-24 23:32:50 [ℹ]  waiting for at least 1 node(s) to become ready in "pool-orakki"
2021-02-24 23:32:50 [ℹ]  nodegroup "pool-orakki" has 1 node(s)
2021-02-24 23:32:50 [ℹ]  node "ip-192-168-51-210.ap-northeast-2.compute.internal" is ready
2021-02-24 23:32:52 [ℹ]  kubectl command should work with "/home/gonghee/.kube/config", try 'kubectl get nodes'2021-02-24 23:32:52 [✔]  EKS cluster "oraksil-prod" in "ap-northeast-2" region is ready

yaml 파일에 정의한대로 클러스터와 관련 AWS 리소스들이 생성된다. 콘솔에 가보면 잘 생성되어있다. 이번엔 노드스펙과 최소 개수도 정의한지라 노드들도 자동으로 생성되었다.

클러스터 설정을 바꾼 후에는 이렇게 적용하면 된다.

$ eksctl upgrade nodegroup -f cluster.yaml

 

Kube Config 설정 (IAM Profile 연동)

이제 클러스터가 생성되었으니 kubectl 명령이 붙는지를 보자.

$ kubectl get pod
The connection to the server localhost:8080 was refused - did you specify the right host or port?

최초에는 ~/.kube/config 설정이 제대로 안되어있어서 안될 수도 있다. (eksctl이 클러스터 생성시 aws access key를 이용해서 기본 설정을 만들어줄 수도 있음)

 

만약 안되어있다면 아래 명령으로 설정을 만들어줄 수 있다.

$ aws eks --region ap-northeast-2 update-kubeconfig --name oraksil-prod
$ kubectl config view --minify
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://A598D40A0F216F39B42122DC5120C976.yl4.ap-northeast-2.eks.amazonaws.com
  name: arn:aws:eks:ap-northeast-2:205311637025:cluster/oraksil-prod
contexts:
- context:
    cluster: arn:aws:eks:ap-northeast-2:205311637025:cluster/oraksil-prod
    user: arn:aws:eks:ap-northeast-2:205311637025:cluster/oraksil-prod
  name: arn:aws:eks:ap-northeast-2:205311637025:cluster/oraksil-prod
current-context: arn:aws:eks:ap-northeast-2:205311637025:cluster/oraksil-prod
kind: Config
preferences: {}
users:
- name: arn:aws:eks:ap-northeast-2:205311637025:cluster/oraksil-prod
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1alpha1
      args:
      - --region
      - ap-northeast-2
      - eks
      - get-token
      - --cluster-name
      - oraksil-prod
      command: aws

 

만약 IAM Profile이 여러개가 있고 default가 아닌 다른 Profile에 대한 Kube Config 설정을 만들고 싶다면 아래처럼 --profile 인자로 aws credential의 Profile 이름을 주면 된다. 

$ cat ~/.aws/credentials 
[default]
aws_access_key_id = <key-id>
aws_secret_access_key = <secret-key>

[gamz]
aws_access_key_id = <key-id>
aws_secret_access_key = <secret-key>

$ aws --profile gamz eks --region ap-northeast-2 update-kubeconfig --name oraksil-prod
Updated context arn:aws:eks:ap-northeast-2:205311637025:cluster/oraksil-prod in /home/gonghee/.kube/config

$ kubectl get pod
error: You must be logged in to the server (Unauthorized)

하지만 이것만으로는 아직 클러스터에 access 를 할 수가 없는데 클러스터에 해당 Profile을 매칭시킬만한 정보가 없기 때문이다. 그래서 아래처럼 aws-auth에 유저 매핑을 추가해줘야한다. (참고)

$ kubectl edit configmap aws-auth -n kube-system

apiVersion: v1
data:
  mapRoles: |
    ...
    ...
  # 이 부분 추가
  mapUsers: |
    - userarn: arn:aws:iam::205304184027:user/gamz
      username: gamz
      groups:
        - system:masters
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system

 

Private ECR 인증 설정

도커 이미지를 올려놓기 위해 ECR(Elastic Container Registry)를 쓰는데 Public ECR인 경우는 push/pull시에 접근에 문제가 없지만 Private일 경우에는 도커 로그인을 해야한다. 아래처럼 하면 된다.

$ aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 201701637074.dkr.ecr.ap-northeast-2.amazonaws.com
WARNING! Your password will be stored unencrypted in /home/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

 

Ingress 구성 (외부 트래픽 수신 준비)

클러스터에 Ingress Nginx 설치

아래 명령으로 ingress-nginx 를 설치하고 나면,

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.41.2/deploy/static/provider/aws/deploy.yaml

ingress-nginx 네임스페이스에 컨트롤러가 실행되고

$ kubectl get pod -n ingress-nginx
NAME                                       READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-l54zv       0/1     Completed   0          2m52s
ingress-nginx-admission-patch-sf6cn        0/1     Completed   0          2m52s
ingress-nginx-controller-ddf87ddc8-2wkwl   1/1     Running     0          2m52s

알아서 ELB도 하나 생성해준다. 

즉, 이렇게 되면 ELB -> k8s Ingress Controller로의 트래픽이 라우팅 되며 k8s의 Ingress 기능을 사용할 수 있게 된다. 더 자세한 내용은 여기를 참고해주세요.

 

Route53

이제 도메인을 생성된 ELB에 연결만 시켜보자. 예를들면, *.orakil.fun 도메인을 ELB 주소로 연결해주면 된다.

 

TLS 설정을 위한 Let's Encrypt + Cert-Manager 셋업

HTTPS 지원를 지원하기 위해서는 보통 인증서를 구매해야하지만 Let's Encrypt 라는 Non-profit 인증서도 있다. 정말 매우 감사. 그런데 k8s 환경에서는 거기에 더해 이 인증서를 Ingress 설정이 생길때 자동으로 붙여주는 Cert-Manager 라는 매우 편리한 플러그인도 있다. 이 두 조합이면 이제 사이트 하나 만들때 인증서 고민할 필요가 전혀 없다.

 

우선 Cert-Manager 를 설치는 간단하다.

$ kubectl apply --validate=true -f https://github.com/jetstack/cert-manager/releases/download/v1.1.0/cert-manager.yaml

 

그런 후에는 ACME Issuer 를 하나 생성해준다. 이 타입은 Automated Certificate Management Environment (ACME) Certificate Authority server라고 하는 서버로 부터 인증서를 자동으로 할당받는 메카니즘을 제공한다.

apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    # You must replace this email address with your own.
    # Let's Encrypt will use this to contact you about expiring
    # certificates, and issues related to your account.
    email: gamzabaw@gmail.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      # Secret resource used to store the account's private key.
      name: letsencrypt-prod
    # Add a single challenge solver, HTTP01 using nginx
    solvers:
    - http01:
        ingress:
          class: nginx

 

Cert-Manager는 ACME로부터 받아온 인증서를 들고 있다가 이 Issuer를 사용하는 Ingress가 생성되면 인증서를 붙여주는 역할을 한다. 가령, Ingress에서는 아래처럼 Issuer 이름을 지정해주면 된다.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: oraksil-azumma-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod      # Issuer 이름 설정
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: api.oraksil.fun
    http:
      paths:
      - path: /
        backend:
          serviceName: oraksil-azumma-svc
          servicePort: 8000
  tls:
  - hosts:
    - api.oraksil.fun
    secretName: oraksil-azumma-cert    # Cert-Manager가 이 이름으로 Secret을 만들어줌

 

어플리케이션 k8s YAML 설정

클러스터 셋업이 잘 완료되었다면 이제 맘껏 앱을 띄우면 된다. 예제만 하나 적어보겠다.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    kubernetes.io/ingress.class: nginx
  name: oraksil-azumma-ingress
spec:
  rules:
  - host: api.oraksil.fun
    http:
      paths:
      - backend:
          serviceName: oraksil-azumma-svc
          servicePort: 8000
        path: /
  tls:
  - hosts:
    - api.oraksil.fun
    secretName: oraksil-azumma-cert

---

apiVersion: v1
kind: Service
metadata:
  labels:
    app: oraksil-azumma
  name: oraksil-azumma-svc
spec:
  ports:
  - port: 8000
    protocol: TCP
    targetPort: 8000
  selector:
    app: oraksil-azumma

---

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: oraksil-azumma
  name: oraksil-azumma
spec:
  replicas: 1
  selector:
    matchLabels:
      app: oraksil-azumma
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: oraksil-azumma
    spec:
      containers:
      - env:
        - name: DB_URI
          value: oraksil:oraksil@(oraksil-db-svc:3306)/oraksil?parseTime=true
        - name: MQRPC_URI
          value: amqp://oraksil:oraksil@oraksil-mq-svc:5672/
        - name: ORAKKI_CONTAINER_IMAGE
          value: 205301683025.dkr.ecr.ap-northeast-2.amazonaws.com/oraksil/orakki:latest
        - name: GIPAN_CONTAINER_IMAGE
          value: 205301683025.dkr.ecr.ap-northeast-2.amazonaws.com/oraksil/gipan:latest
        - name: TURN_URI
          value: turn:211.107.108.230:3478?transport=tcp
        ...
        ...
        image: 205301683025.dkr.ecr.ap-northeast-2.amazonaws.com/oraksil/azumma:latest
        imagePullPolicy: Always
        name: oraksil-azumma
        ports:
        - containerPort: 8000
        readinessProbe:
          httpGet:
            path: /api/v1/packs
            port: 8000
          initialDelaySeconds: 2
          periodSeconds: 2
          timeoutSeconds: 5

 

EKS 콘솔에서 클러스터 리소스 조회되게 하기 (옵션)

Root 유저는 문제 없는데 IAM로 추가된 유저가 EKS 콘솔을 조회할때 Pod 목록 같은데 잘 안보일 수 있다. 이때는 해당 유저 혹은 유저가 소속된 그룹에 Inline Policy를 다음처럼 하나 추가해주면 된다.

# AmazonEKSAdminPolicy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "iam:PassedToService": "eks.amazonaws.com"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "eks:DescribeNodegroup",
                "eks:ListNodegroups",
                "eks:DescribeCluster",
                "eks:ListClusters",
                "eks:AccessKubernetesApi",
                "eks:ListUpdates",
                "eks:ListFargateProfiles",
                "eks:ListAddons",
                "eks:DescribeAddonVersions",
                "ssm:GetParameter",
                "iam:ListRoles"
            ],
            "Resource": "*"
        }
    ]
}

특이사항

나는 Pod의 Requested CPU/Memory 수치가 충분히 낮다면 노드 하나에도 컨테이너를 모두 우겨넣을 수 있을 줄 알았다. 그런데 리소스는 충분히 여유 있는 상황인데도 Pod이 계속 Pending 상태인 상황이 있었다. 어떤 이슈인지 상상도 못하고 있다가 이런 자료를 찾았는데 이거 꽤나 삽질 포인트가 될만해서 공유해본다.

 

즉, 한 인스턴스에 뜰 수 있는 Pod의 개수는 ENI(Elastic Network Interface)의 개수와 이 ENI당 할당 가능한 IP 수에 영향을 받는 다는 것이고 이건 또한 인스턴스 타입에 영향을 받는다. 링크를 따라가보면 대략 공식이 있긴 한데 이것을 인스턴스타입별로 미리 계산해둔 목록도 있다.

 

AWS 아닌 옵션?

Kubernetes를 사용함에 있어서 대표적인 장점 중에 하나는 특정 Cloud Provider에 Lock-in이 되지 않는다는 점이다. 최근 대표적인 Provider들은(AWS / Azure / GCP / Digital Ocean) 모두 Kubernetes를 지원하며 각자로 유인하기 위한 옵션들을 제공한다.

 

오락실도 처음엔 k8s의 본고장이기도한 GCP/GKE를 이용해보려고 했다. 일단 k8s management 비용이 기본적으로 무료(하나의 Zone에 한에)라는 부분이 아주 큰 메리트다. (AWS는 이것만 하루에 3-4달러가 나온다)

 

그러나 GCP Seoul Region 에서 GKE를 사용해봤을때 다음과 같은 눈에 띄는 이슈들이 있어서 접었다.

  • 인스턴스 타입의 선택폭이 너무 좁다 (가령, Compute Optimized 인스턴스가 없음)

  •  초기 vCPU/Memory Quota가 너무 적었다. (12 vCPU 정도였나?) 그래서 이걸 늘릴려고 Form을 채워서 요청을 했는데 아직 운영 중인 데이터가 없어서 늘리는 것에 대해서 판단할 수 없다고 답변이 왔다. 초기 Quota가 넉넉한 것도 아니고 무엇보다 내가 돈내고 쓰겠다는데.. 그에 비해 너무 번거로운 부분이 마이너스였다.

  • 또한 k8s을 쓰려면 Container Registry는 사용은 필수라 GCP가 제공하는 GCR을 써봤는데 이게 Object Storage를 활용해서 만든거라 그런지 아니면 뭔가 다른 이슈가 있는지 몇십MB 이미지 하나를 Push하는데 1분 가까이 소요됐다. 이것도 매번 너무 지치게 하는 포인트였다.

그리고 AWS로 넘어왔는데 위의 세 문제는 당연히 없고 너무 쾌적했다. AWS가 클라우드 점유율이 높은 이유가 있긴한가보다. k8s가 보편화되면 GCP가 잡아먹을 줄 알았는데 그게 또 쉽지 않나보다라는 생각도 들고. 하지만 AWS가 확실히 비싸긴 하다는거.. 인스턴스 세개정도로 최소한으로 돌리는데 한달에 $160이상이 들더라.