데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      SpringBoot 어플리케이션 helm chart 만들기

      seungkyua 23.12.20
      3,690 21 0
      DEVOTEE 요약
      SpringBoot 어플리케이션에 대한 Helm 차트를 완성하기 위해 deployment, 서비스명 지정, 네임스페이스 지정, replicas 수, 이미지 경로 지정 등의 기능을 고려해야 한다. 고려해야 할 내용 중 helm.sh/chart, app.kubernetes.io/instance 등이 포함된 부분은 레이블에서 사용하며, deployment.yaml 에서는 image, env, port 등이 지정되어 있다. 또한 동일한 fullnameOverride 값을 사용하여 Service 이름을 지정하고, 원하는 방식을 생성할 수 있다.
      DEVOTEE 추천 블로그

      SpringBoot 어플리케이션에 대한 helm chart 는 deployment 로 타입으로 쉽게 만들 수 있다.

      이 때 최소한 다음과 같은 기능을 고려해서 만들어야 한다.

      1. deployment 혹은 service 명 지정

      2. 네임스페이스 지정

      3. Replicas 수 지정

      4. 이미지 경로 지정

      5. 스케줄링 기능

        1. nodeSelector 사용

        2. nodeAffinity 사용

        3. podAffinity 사용

        4. taint / toleration 사용

      6. environment 설정 기능

      7. pod 에 대한 annotations 와 labels 지정

      8. Pod Resource 지정

        1. reqeuests 지정

        2. limits 지정

      9. Service 생성

        1. Type 변경

        2. 포트 변경

      10. ingress 생성 여부

      아래는 customer-service 라는 springboot 어플리케이션에 대한 helm chart 디렉토리 구조이다.

          customer-service
          ├── Chart.yaml
          ├── templates
          │   ├── NOTES.txt
          │   ├── _helpers.tpl
          │   ├── deployment.yaml
          │   ├── hpa.yaml
          │   ├── ingress.yaml
          │   ├── service.yaml
          │   └── serviceaccount.yaml
          └── values.yaml

      먼저 Chart.yaml 을 보자.

      일반적인 메타데이터가 들어있다.

      # Chart.yaml
      
      apiVersion: v2
      name: customer-service
      description: A Helm chart for MSA Pattern customer-service
      type: application
      version: 1.0.0
      appVersion: "1.0.0"

      values.yaml 은 다음 값들이 들어 있다.

      사실 value 값만 봐도 무엇을 지정할 수 있는지 쉽게 알 수 있다.

      # values.yaml
      
      replicaCount: 1
      
      image:
        repository: seungkyua/customer-service
        pullPolicy: Always
        tag: "1.0.0"
      
      imagePullSecrets: []
      nameOverride: ""
      fullnameOverride: "customer-service"
      
      serviceAccount:
        create: true
        automount: true
        annotations: {}
        name: ""
      
      podAnnotations: {}
      podLabels: {}
      
      podSecurityContext: {}
        # fsGroup: 2000
      
      securityContext: {}
        # capabilities:
        #   drop:
        #   - ALL
        # readOnlyRootFilesystem: true
        # runAsNonRoot: true
        # runAsUser: 1000
      
      env:
        - name: JAVA_OPTS
          value: "-Dspring.profiles.active=prod"
      
      service:
        type: LoadBalancer
        port: 8082
      
      ingress:
        enabled: false
        className: "nginx"
        annotations: {}
          # kubernetes.io/ingress.class: nginx
          # kubernetes.io/tls-acme: "true"
        hosts:
          - host: customer-service.taco-cat.xyz
            paths:
              - path: /
                pathType: ImplementationSpecific
        tls: []
        #  - secretName: chart-example-tls
        #    hosts:
        #      - chart-example.local
      
      resources: {}
        # limits:
        #   cpu: 100m
        #   memory: 128Mi
        # requests:
        #   cpu: 100m
        #   memory: 128Mi
      
      autoscaling:
        enabled: false
        minReplicas: 1
        maxReplicas: 100
        targetCPUUtilizationPercentage: 80
        # targetMemoryUtilizationPercentage: 80
      
      volumes: []
      # - name: foo
      #   secret:
      #     secretName: mysecret
      #     optional: false
      
      volumeMounts: []
      # - name: foo
      #   mountPath: "/etc/foo"
      #   readOnly: true
      
      nodeSelector: {}
      
      tolerations: []
      
      affinity: {}

      _helpers.tpl 파일에는 기본 name 에 대한 함수들이 정의되어 있다.

      # _helpers.tpl
      
      {{- define "springboot-docker.name" -}}
      {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
      {{- end }}
      
      {{- define "springboot-docker.fullname" -}}
      {{- if .Values.fullnameOverride }}
      {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
      {{- else }}
      {{- $name := default .Chart.Name .Values.nameOverride }}
      {{- if contains $name .Release.Name }}
      {{- .Release.Name | trunc 63 | trimSuffix "-" }}
      {{- else }}
      {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
      {{- end }}
      {{- end }}
      {{- end }}

      values.yaml 파일에 정의된 fullnameOverride 값을 쓰고 이게 없으면 chart 이름과 릴리즈 이름을 사용한다.

      chart 에 지정한 이름이 릴리즈 이름에 포함되면 중복을 피하기 위해서 릴리즈 이름만 사용한다.

      릴리즈 이름에 차트 이름이 포함되어 있지 않으면 릴리즈 이름과 차트을 합쳐서 이름을 만든다.

      아래는 리소스에 대해 기본적인 label 을 추가하는 코드이다.

      # _helpers.tpl
      
      {{- define "springboot-docker.labels" -}}
      helm.sh/chart: {{ include "springboot-docker.chart" . }}
      {{ include "springboot-docker.selectorLabels" . }}
      {{- if .Chart.AppVersion }}
      app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
      {{- end }}
      app.kubernetes.io/managed-by: {{ .Release.Service }}
      {{- end }}
      
      {{- define "springboot-docker.chart" -}}
      {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
      {{- end }}
      
      {{- define "springboot-docker.selectorLabels" -}}
      app.kubernetes.io/name: {{ include "springboot-docker.name" . }}
      app.kubernetes.io/instance: {{ .Release.Name }}
      {{- end }}

      이제 deployment.yaml 을 보자.

      #deployment.yaml
      
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: {{ include "springboot-docker.fullname" . }}
        labels:
          {{- include "springboot-docker.labels" . | nindent 4 }}
      spec:
        {{- if not .Values.autoscaling.enabled }}
        replicas: {{ .Values.replicaCount }}
        {{- end }}
        selector:
          matchLabels:
            {{- include "springboot-docker.selectorLabels" . | nindent 6 }}
        template:
          metadata:
            {{- with .Values.podAnnotations }}
            annotations:
              {{- toYaml . | nindent 8 }}
            {{- end }}
            labels:
              {{- include "springboot-docker.labels" . | nindent 8 }}
      	    {{- with .Values.podLabels }}
              {{- toYaml . | nindent 8 }}
            {{- end }}
          spec:
            {{- with .Values.imagePullSecrets }}
            imagePullSecrets:
              {{- toYaml . | nindent 8 }}
            {{- end }}
            serviceAccountName: {{ include "springboot-docker.serviceAccountName" . }}
            securityContext:
              {{- toYaml .Values.podSecurityContext | nindent 8 }}
            containers:
              - name: {{ .Chart.Name }}
                securityContext:
                  {{- toYaml .Values.securityContext | nindent 12 }}
                image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
                {{- with .Values.env }}
                env:
                  {{- toYaml . | nindent 12 }}
                {{- end }}
                imagePullPolicy: {{ .Values.image.pullPolicy }}
                ports:
                  - name: http
                    containerPort: {{ .Values.service.port }}
                    protocol: TCP
                livenessProbe:
                  httpGet:
                    path: /healthz
                    port: http
                readinessProbe:
                  httpGet:
                    path: /healthz
                    port: http
                resources:
                  {{- toYaml .Values.resources | nindent 12 }}
                {{- with .Values.volumeMounts }}
                volumeMounts:
                  {{- toYaml . | nindent 12 }}
                {{- end }}
            {{- with .Values.volumes }}
            volumes:
              {{- toYaml . | nindent 8 }}
            {{- end }}
            {{- with .Values.nodeSelector }}
            nodeSelector:
              {{- toYaml . | nindent 8 }}
            {{- end }}
            {{- with .Values.affinity }}
            affinity:
              {{- toYaml . | nindent 8 }}
            {{- end }}
            {{- with .Values.tolerations }}
            tolerations:
              {{- toYaml . | nindent 8 }}
            {{- end }}

      전체 소스는 위와 같으며, 이제 조금씩 나눠서 살펴보자.

      name 은 앞에서 설명한 values.yaml의 fullnameOverride 으로 지정할 수 있다.

      labels 은 특정 지정된 labels 로 되어 있는데 이는 selector 의 matchLabels 와 template 의 labels 가 같아야 하기 때문이다.

      pod 에 줄 수 있는 annotation 과 추가로 줄 수 있는 labels 가 toYmal 을 사용하여 values.yaml 의 podAnnotations 과 podLabels 값으로 지정할 수 있게 하였다.

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: {{ include "springboot-docker.fullname" . }}
        labels:
          {{- include "springboot-docker.labels" . | nindent 4 }}
      spec:
        {{- if not .Values.autoscaling.enabled }}
        replicas: {{ .Values.replicaCount }}
        {{- end }}
        selector:
          matchLabels:
            {{- include "springboot-docker.selectorLabels" . | nindent 6 }}
        template:
          metadata:
            {{- with .Values.podAnnotations }}
            annotations:
              {{- toYaml . | nindent 8 }}
            {{- end }}
            labels:
              {{- include "springboot-docker.labels" . | nindent 8 }}
      	    {{- with .Values.podLabels }}
              {{- toYaml . | nindent 8 }}
            {{- end }}

      containers 를 추가로 보면 image 를 values.yaml 로 지정할 수 있게 되어 있다.

      이는 보통 프로젝트마다 가지고 있는 이미지 리파지토리를 활용할 수 있게 하기 위해서 변경 가능하게 만든 것이다.


      그리고 container 의 env 를 추가할 수 있게 되어 있다.

      port 는 컨테이너 포트와 Service 의 포트를 다르게 가져갈 수 도 있지만 일단은 둘 다 동일하게 사용하는 방식으로 맞추었다.

      liveness 와 readiness 는 경로와 포트가 수정할 수 없게 되어 있는데 이 또한 변수로 만들 수 있다.

      여기서는 springboot 소스에서 해당 path 로 헬스 체크를 열어 놓았기 때문에 하드 코딩 하였다.


      그리고 container 를 띄울 때 자원에 대한 requests, limits 을 지정할 수 있게 하였다.

            containers:
              - name: {{ .Chart.Name }}
                securityContext:
                  {{- toYaml .Values.securityContext | nindent 12 }}
                image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
                {{- with .Values.env }}
                env:
                  {{- toYaml . | nindent 12 }}
                {{- end }}
                imagePullPolicy: {{ .Values.image.pullPolicy }}
                ports:
                  - name: http
                    containerPort: {{ .Values.service.port }}
                    protocol: TCP
                livenessProbe:
                  httpGet:
                    path: /healthz
                    port: http
                readinessProbe:
                  httpGet:
                    path: /healthz
                    port: http
                resources:
                  {{- toYaml .Values.resources | nindent 12 }}

      추가로 volume 과 스케줄링을 위한 소스를 보자.

      volumes 와 volumeMounts, nodeSelector, affinity, tolerations 는 toYaml 과 nindent 를 사용해서 values.yaml 에 넣은 값들을 그대로 활용할 수 있게 만들었다.

                {{- with .Values.volumeMounts }}
                volumeMounts:
                  {{- toYaml . | nindent 12 }}
                {{- end }}
            {{- with .Values.volumes }}
            volumes:
              {{- toYaml . | nindent 8 }}
            {{- end }}
            {{- with .Values.nodeSelector }}
            nodeSelector:
              {{- toYaml . | nindent 8 }}
            {{- end }}
            {{- with .Values.affinity }}
            affinity:
              {{- toYaml . | nindent 8 }}
            {{- end }}
            {{- with .Values.tolerations }}
            tolerations:
              {{- toYaml . | nindent 8 }}
            {{- end }}

      service.yaml 에 대한 내용도 살펴보자.

      deployment 를 만들면 네트워크 통신을 위해서 반드시 service.yaml 도 만들어야 한다.

      Service 이름은 동일하게 fullnameOverride 값으로 지정할 수 있고 type 을 넣어서 원하는 방식을 생성할 수 있게 하였다.

      앞에서도 설명하였지만 deployment 의 container port 와 service 의 port 가 동일하게 사용된다.

      둘을 다르게 지정하고 싶으면 value.yaml 에서 키 값을 다르게 지정하면 된다.

      apiVersion: v1
      kind: Service
      metadata:
        name: {{ include "springboot-docker.fullname" . }}
        labels:
          {{- include "springboot-docker.labels" . | nindent 4 }}
      spec:
        type: {{ .Values.service.type }}
        ports:
          - port: {{ .Values.service.port }}
            targetPort: http
            protocol: TCP
            name: http
        selector:
          {{- include "springboot-docker.selectorLabels" . | nindent 4 }}

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      seungkyua 님의 최신 블로그

      더보기

      DEVOTEE 추천 블로그

      동영상 기고하기