쿠버네티스 일반/Network

Tailscale VPN for 쿠버네티스 & VPC

Jerry_이정훈 2022. 9. 26. 09:44
728x90

최근에 EKS, AWS VPC 환경에서 Tailscale VPN을 잘 사용하고 있어 내용을 공유한다.

 

TL;DR

  • Tailscale VPN을
    1) EKS 환경에서 LoadBalancer, Ingress 등을 추가 설정하지 않고 ClusterIP 타입 그대로 VPN을 사용하는 사용자에게 외부 노출
    2) EC2 사용하는 VPC의 전체 네트워크를 Tailscale VPN으로 연결
  • EC2 VM이 아닌 쿠버네티스 파드로 실행할 수 있어 EC2 VM 비용을 절감할 수 있다. 추가로 Deployment로 실행하면 파드 다운 시 자동 실행되니 관리 포인트도 줄어든다.
  • ClusterIP는 YAML 파일에서 ClusterIP를 고정할 수 있어, 서비스를 재시작해도 IP가 변경되지 않는다. 쿠버네티스 DNS 이름을 그대로 사용할 수 있으면 Best인데, 아직 그 기능은 지원하지 않는다. (Tailscale의 Magic DNS 기능이 있는데 이건 호스트네임을 사용하는 거라 쿠버네티스의 서비스 이름과 관련없다.)
  • 속도도 잘 나오고 VPN On/Off 시 네트워크 끊김(ex: OpenVPN은 재연결하면 네트워크가 끊겼다)도 없고 만족스럽다
  • 유료라 아쉽기는 하지만 사용자 당 월 $6 수준이라(Team Plan 기준) 크게 부담스럽지는 않다.  
  • 기본 설정의 EKS 쿠버네티스 ClusterIP 대역은 10.100.0.0/16, 172.20.0.0/16 인데 이를 수정하지 않으면 서로 다른 클러스터이지만 ClusterIP의 IP 대역이 동일하게 된다. 그러면 2개의 클러스터에 Tailscale을 동시에 사용할 수 없고 하나의 클러스터만 사용해야 한다.(나도 이런 상황..) 이걸 변경하려면 클러스터를 다시 설치해야하는... EKS에서 Random하게 혹은 기존과 겹치지 않게 만들어 주었으면 좋았는데 아쉽다. 

구성 내역

Tailsacle은 ClusterIP, VPN 등 특정 IP 대역 전체를 VPN으로 연결하기 위해서는 'Subnet Router' 으로 설정한다. subnet router만 설정하면 네트워크 전체가 VPN으로 연결하니 작업 시간이 줄어든다. 하지만 Mesh 환경으로 각 VM, Pod에 각각 설정하는 것보다 안정성, 성능 등은 조금 떨어질 수 있다. 시간이 줄어드니.. 난 subnet router로 설정.

from : tailscale.com

 

Tailscale 공식 홈페이지 가이드는 Tailscale을 쿠버네티스에서 사용하려면 Make 파일을 이용한다. 하지만 Make 파일은 직관적이지 않아서 나는 Secret, SA 등을 YAML 파일로 만들었다. 아마도 다른 사람들도 YAML 파일을 보는게 좀 더 이해가 쉬울 것 같아 내가 만든 YAML 파일을 공유한다.

 

먼저 인증을 위한 Secret 키를 만든다. Tailscale 관리자 페이지에서 Tailscale 인증키(sample: TS_AUTH_KEY: tskey-0123456789abcdef)를 확인할 수 있다. 아래는 sample key다.

tailscale-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: tailscale-auth
stringData:
  TS_AUTH_KEY: tskey-0123456789abcdef

해당 이름으로 Secret을 만든다.

k apply -f tailscale-secret.yaml

다음으로 Secret을 사용하기 위해서 RBAC을 만든다. 

# Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: tailscale
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["secrets"]
  # Create can not be restricted to a resource name.
  verbs: ["create"]
- apiGroups: [""] # "" indicates the core API group
  resourceNames: ["tailscale-auth"]
  resources: ["secrets"]
  verbs: ["get", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: tailscale
subjects:
- kind: ServiceAccount
  name: tailscale
roleRef:
  kind: Role
  name: tailscale
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: tailscale

이제 설정이 완료되었으니 Tailscale 파드를 실행하기 위한 Deployment YAML을 만든다. 

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    kubernetes.io/psp: eks.privileged
  labels:
    app: tailscale
  name: tailscale-subnet-router
  namespace: tailscale
spec:
  replicas: 1
  # strategy:
  #   type: Recreate
  selector:
    matchLabels:
      app: tailscale  # POD label과 일치
  template:    
    metadata:
      labels:
        app: tailscale # Selector label과 일치
    spec:
      containers:
      - env:
        - name: TS_KUBE_SECRET
          value: tailscale-auth
        - name: TS_USERSPACE
          value: "true"
        - name: TS_AUTH_KEY
          valueFrom:
            secretKeyRef:
              key: AUTH_KEY
              name: tailscale-auth
              optional: true
        - name: TS_ROUTES
          # value: 172.20.0.0/16
          value: 10.10.0.0/16,10.11.0.0/16
        image: ghcr.io/tailscale/tailscale:v1.28
        name: tailscale
        securityContext:
          runAsGroup: 1000
          runAsUser: 1000
        volumeMounts:
        - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
          name: kube-api-access-lbkdr
          readOnly: true
      tolerations:
      - effect: NoExecute
        key: node.kubernetes.io/not-ready
        operator: Exists
        tolerationSeconds: 300
      - effect: NoExecute
        key: node.kubernetes.io/unreachable
        operator: Exists
        tolerationSeconds: 300
      preemptionPolicy: PreemptLowerPriority
      priority: 0
      serviceAccountName: tailscale
      volumes:
      - name: kube-api-access-lbkdr
        projected:
          sources:
          - serviceAccountToken:
              expirationSeconds: 3607
              path: token
          - configMap:
              items:
              - key: ca.crt
                path: ca.crt
              name: kube-root-ca.crt
          - downwardAPI:
              items:
              - fieldRef:
                  fieldPath: metadata.namespace
                path: namespace
  • TS_KUBE_SECRET, TS_USERPACE, TS_AUTH_KEY + ServiceAccount
    Tailscale 이미지에 정의된 위 환경 변수를 앞서 만든 Secret, RBAC에서 정의되어 있다.
  • TS_ROUTES
    중요한 건 TS_ROUTES 정보인데 해당 IP 대역에 EKS에서 사용하는 ClusterIP의 IP 대역 정보를 지정한다. 필자는 EKS를 사용하는데 EKS 설정 파일에 ClusterIP 정보를 확인할 수 있다. EKS의 경우 기본 설정은 10.100.0.0/16 또는 172.20.0.0/16 이다.
You can only specify this option when using the IPv4 address family and only at cluster creation. If you don't specify this, then Kubernetes assigns service IP addresses from either the 10.100.0.0/16 or 172.20.0.0/16 CIDR blocks.
https://docs.aws.amazon.com/eks/latest/userguide/create-cluster.html

필자는 아쉽게도 Default ClusterIP 대역을 사용해서 복수의 EKS 클러스터가 같은 대역을 사용하였다. 그래서 ClusterIP 대역이 겹쳐서 ClusterIP 용도의 VPN을 2개 같이 사용하지 못하여 하나의 클러스터에만 사용하게 되었다.

 

추가로 노드가 포함된 VPC 대역의 IP도 TS_ROUTES에 등록하면 해당 대역도 VPN으로 연결할 수 있다.

 

이제 실행을 하면 파드가 정상적으로 실행된다. 필자는 tailscale 네임스페이스를 별도로 만들고 실행하였다.

$ (⎈ |saas-stage:kafka) k ns tailscale

$ (⎈ |saas-stage:tailscale) k get pod
NAME                                      READY   STATUS    RESTARTS   AGE
tailscale-subnet-router-5c47d4cd7-dqcjf   1/1     Running   0          23h

이제 로컬 PC에서 Tailscale VPN을 연결하면 EKS의 ClusterIP 대역과 직접 통신이 가능하다.

# default 네임스페이스로 변경하고
$ (⎈ |saas-stage:tailscale) k ns default

# ClusterIP의 IP를 확인하고
$ (⎈ |saas-stage:default) k get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
nginx-svc    ClusterIP   172.20.206.121   <none>        80/TCP    6m8s

# ClusterIP로 직접 접속하면 정상적으로 접속된다.
$ (⎈ |saas-stage:default) curl 172.20.206.121
<!DOCTYPE html>
<html>
<head>
<title>Hello World</title>

물론 웹 브라우저로도 확인할 수 있다.

브라우저 접속

이상 Tailscale 사용 사례이다. VPN 솔루션을 검토한다면 비록 유료이지만 좋은 솔루션 같다.

반응형