Traefik, Let's encrypt 및 Zookeeper로 개인 클라우드 실행

Traefik을 사용한 Kubernetes 인그레스



last blog post에서 언급한 바와 같이 저는 가능할 때마다 클라우드 서비스에 얽매이지 않는 기술을 사용하여 자체 클라우드에 대한 공급자 중립 설정에 집중하고 싶습니다.

Google 클라우드는 기본적으로 로드 밸런싱된 HTTP 인그레스를 제공하지만 작은 노드를 실행하는 것과 비교할 때 분명히 매우 비싸며 kubernetes 인그레스에 Traefik을 사용하는 것에 대해 좋은 소식만 들었습니다.

Traefik을 설정하기 위해 약간 수정하여 Manuel's excellent guide을 따랐습니다(기사 끝에서 최종 파일을 찾을 수 있습니다.)

HTTP와 Let's encrypt



Traefik에는 Let's Encrypt을 사용하여 HTTPS 인증서를 자동으로 가져오고 갱신하기 위한 지원 기능이 내장되어 있습니다. HTTPS는 모범 사례이며 HTTP2 및 PWA에 대한 요구 사항이므로 Traefik 문서에서 example configurations을 사용하여 설정했습니다.

나는 Traefik에 대해 단 하나의 노드를 사용하고 있었기 때문에 노드가 실행되는 동안 인증서를 저장하는 로컬 acme.json 파일의 쉬운 설정을 선택했습니다.

GKE 선점형 노드, 나만의 혼돈 원숭이



비용을 절감하기 위해 GKE에서 내 kubernetes 클러스터를 구동하는 노드로 "선점형 VM"을 사용하기로 했습니다. Google 문서에 따르면 "선점형 VM은 최대 24시간 동안 지속되고 가용성을 보장하지 않는 Google Compute Engine VM 인스턴스입니다."이는 내 kubernetes 클러스터의 노드가 임의로 다운되고 24시간 이상 가동되지 않음을 의미합니다. 이것은 분명히 프로덕션 설정에 대한 현명한 결정은 아니지만 저는 이를 수용하고 복원력 있는 코드를 작성하도록 강제하는 노드가 내 노드"chaos monkey"로 내려가는 것을 고려하기로 선택했습니다.

내가 만난 구체적인 예: Let's encrypt 프로덕션 API에는 일주일에 동일한 URL에 대해 5개의 인증서를 요청하는 속도 제한이 있습니다. 내 초기 순진한 설정이 내 Traefik 노드가 종료될 때마다 손실된 인증서를 어디에도 저장하지 않았기 때문입니다. Traefik이 시작 시 문제 없이 인증서를 재생성하는 동안... 5번의 시작 후 속도 제한에 도달했고 인증서 없이 안전하지 않은 경고를 받았습니다.

Zookeeper와 Traefik의 공유 K/V 매장



Traefik에 대한 공유 키/값 저장소를 입력합니다. 어쨌든 클러스터 모드에서 Traefik을 실행하려면 하나를 사용해야 합니다(제 설정을 쉽게 확장할 수 있다고 생각합니다). 또한 Traefik이 다시 시작될 때 더 이상 사라지지 않는 K/V 스토어에 생성된 인증서를 저장할 수 있음을 의미합니다.

나는 Zookeeper에 대한 이전 경험이 있고 설정이 비교적 고통스럽지 않았기 때문에 그것을 사용했습니다.

설정을 위한 모든 Kubernetes yaml 파일



마지막으로 블로그 게시물의 핵심은 GKE 클러스터에 직접 배포할 수 있는 yaml 파일의 전체 설정입니다.

먼저 Zookeeper 설정



이 우수한 리소스에서: https://github.com/kow3ns/kubernetes-zookeeper/blob/master/manifests/README.md

apiVersion: v1
kind: Service
metadata:
  name: zk-hs
  labels:
    app: zk
spec:
  ports:
  - port: 2888
    name: server
  - port: 3888
    name: leader-election
  clusterIP: None
  selector:
    app: zk
---
apiVersion: v1
kind: Service
metadata:
  name: zk-cs
  labels:
    app: zk
spec:
  ports:
  - port: 2181
    name: client
  selector:
    app: zk
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: zk
spec:
  serviceName: zk-hs
  replicas: 1
  podManagementPolicy: Parallel
  updateStrategy:
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: zk
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zk
              topologyKey: "kubernetes.io/hostname"
      containers:
      - name: kubernetes-zookeeper
        imagePullPolicy: Always
        image: "gcr.io/google_containers/kubernetes-zookeeper:1.0-3.4.10"
        resources:
          requests:
            memory: "200M"
            cpu: "0.3"
        ports:
        - containerPort: 2181
          name: client
        - containerPort: 2888
          name: server
        - containerPort: 3888
          name: leader-election
        command:
        - sh
        - -c
        - "start-zookeeper \
          --servers=1 \
          --data_dir=/var/lib/zookeeper/data \
          --data_log_dir=/var/lib/zookeeper/data/log \
          --conf_dir=/opt/zookeeper/conf \
          --client_port=2181 \
          --election_port=3888 \
          --server_port=2888 \
          --tick_time=2000 \
          --init_limit=10 \
          --sync_limit=5 \
          --heap=512M \
          --max_client_cnxns=60 \
          --snap_retain_count=3 \
          --purge_interval=12 \
          --max_session_timeout=40000 \
          --min_session_timeout=4000 \
          --log_level=INFO"
        readinessProbe:
          exec:
            command:
            - sh
            - -c
            - "zookeeper-ready 2181"
          initialDelaySeconds: 10
          timeoutSeconds: 5
        livenessProbe:
          exec:
            command:
            - sh
            - -c
            - "zookeeper-ready 2181"
          initialDelaySeconds: 10
          timeoutSeconds: 5
        volumeMounts:
        - name: datadir
          mountPath: /var/lib/zookeeper
      securityContext:
        runAsUser: 1000
        fsGroup: 1000
  volumeClaimTemplates:
  - metadata:
      name: datadir
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 5Gi


Traefik에 대한 권한




# create Traefik cluster role
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
 name: traefik-ingress-controller
rules:
  - apiGroups:
      - ""
    resources:
      - services
      - endpoints
      - secrets
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
---
# create Traefik service account
kind: ServiceAccount
apiVersion: v1
metadata:
  name: traefik-ingress-controller
  namespace: default
---
# bind role with service account
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: traefik-ingress-controller
subjects:
- kind: ServiceAccount
  name: traefik-ingress-controller
  namespace: default


Traefik 구성



"클라이언트 서비스"(cs)에 대한 서비스 주소를 사용하는 사육사 구성과 Let's encrypt here 구성에 유의하십시오.

# define Traefik configuration
kind: ConfigMap
apiVersion: v1
metadata:
  name: traefik-config
data:
  traefik.toml: |
    # traefik.toml
    defaultEntryPoints = ["http", "https"]
    [entryPoints]
      [entryPoints.http]
        address = ":80"
        [entryPoints.http.redirect]
          entryPoint = "https"
      [entryPoints.https]
      address = ":443"
        [entryPoints.https.tls]

      [zookeeper]
        endpoint = "zk-cs.default.svc.cluster.local:2181"
        watch = true
        prefix = "traefik"

      [acme]
      email = "[email protected]"
      storage = "traefik/acme/account"
      onHostRule = true
      caServer = "https://acme-v02.api.letsencrypt.org/directory"
      acmeLogging = true
      entryPoint = "https"
        [acme.httpChallenge]
        entryPoint = "http"

      [[acme.domains]]
        main = "your.domain.com"


Traefik용 배포



내 개발 설정에서 비용을 절약하기 위해 여기에서 하나의 복제본만 실행하지만 임의의 노드가 다운되고 모든 것이 잘 작동하는 경우에도 시간의 100%를 유지하는지 테스트하기 위해 최대 3개까지 확장했습니다 :).

# declare Traefik deployment
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: traefik-ingress-controller
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: traefik-ingress-controller
    spec:
      serviceAccountName: traefik-ingress-controller
      terminationGracePeriodSeconds: 60
      volumes:
        - name: config
          configMap:
            name: traefik-config
      containers:
      - name: traefik
        image: "traefik:1.7.14"
        volumeMounts:
          - mountPath: "/etc/traefik/config"
            name: config
        args:
        - --configfile=/etc/traefik/config/traefik.toml
        - --kubernetes
        - --logLevel=INFO


Traefik 서비스




# Declare Traefik ingress service
kind: Service
apiVersion: v1
metadata:
  name: traefik-ingress-controller
spec:
  selector:
    app: traefik-ingress-controller
  ports:
    - port: 80
      name: http
    - port: 443
      name: tls
  type: LoadBalancer


최종 결과



traefik 및 zookeeper의 최종 워크로드



그리고 kubernetes가 들어옵니다(데모로 사용한 앱은 무시하세요).



나에 대해서



저는 풀 스택 개발자이자 디지털 제품 애호가입니다. 저는 프리랜서 작업이 가능하며 항상 다음 흥미로운 프로젝트를 찾고 있습니다 :).

email ([email protected]) 또는 트위터를 통해 온라인으로 연락할 수 있습니다.

좋은 웹페이지 즐겨찾기