Helm: ํˆฌ๊ตฌ 3 - nginx ์—…๊ทธ๋ ˆ์ด๋“œ - spec.clusterIP: ์ž˜๋ชป๋œ ๊ฐ’: "": ํ•„๋“œ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์— ๋งŒ๋“  2019๋…„ 09์›” 06์ผ  ยท  67์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: helm/helm

๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฐจํŠธ๋ฅผ ์„ค์น˜/์—…๊ทธ๋ ˆ์ด๋“œํ•ฉ๋‹ˆ๋‹ค.

./helm ์—…๊ทธ๋ ˆ์ด๋“œ --์„ค์น˜
--set rbac.create=false
--set controller.replicaCount=2
--set controller.service.loadBalancerIP=$ip
--main-ingress ์•ˆ์ •/nginx-ingress ๋Œ€๊ธฐ

(์—ฌ๊ธฐ์„œ $ip๋Š” IP, ์˜ˆ: 10.0.0.1)

์ด๋Š” CI/CD ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ์ˆ˜ํ–‰๋˜๋ฏ€๋กœ ์ฒ˜์Œ์—๋Š” ์„ค์น˜ํ•˜๊ณ  ๋‹ค์Œ์—๋Š” ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

์ž˜ ์„ค์น˜๋ฉ๋‹ˆ๋‹ค. ๋‘ ๋ฒˆ์งธ ์‹คํ–‰์—์„œ๋Š” ๋‹ค์Œ์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

_client.go:339: ํŒจ์น˜ํ•  ์ˆ˜ ์—†์Œ ์„œ๋น„์Šค: "main-ingress-nginx-ingress-controller"(์„œ๋น„์Šค "main-ingress-nginx-ingress-controller"๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์Œ: spec.clusterIP: ์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฐ’: "": ํ•„๋“œ๊ฐ€ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€ )
client.go:358: --force๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ•์ œ๋กœ ๋ฆฌ์†Œ์Šค ์žฌ์ƒ์„ฑ
client.go:339: ์„œ๋น„์Šค๋ฅผ ํŒจ์น˜ํ•  ์ˆ˜ ์—†์Œ: "main-ingress-nginx-ingress-default-backend"(์„œ๋น„์Šค "main-ingress-nginx-ingress-default-backend"๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์Œ: spec.clusterIP: ์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฐ’: "" : ํ•„๋“œ๋Š” ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Œ)
client.go:358: --force๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ•์ œ๋กœ ๋ฆฌ์†Œ์Šค ์žฌ์ƒ์„ฑ
์˜ค๋ฅ˜: ์—…๊ทธ๋ ˆ์ด๋“œ ์‹คํŒจ: ์„œ๋น„์Šค "main-ingress-nginx-ingress-controller"๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์Œ: spec.clusterIP: ์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฐ’: "": ํ•„๋“œ๊ฐ€ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅ && ์„œ๋น„์Šค "main-ingress-nginx-ingress-default-backend"๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์Œ : spec.clusterIP: ์ž˜๋ชป๋œ ๊ฐ’: "": ํ•„๋“œ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋˜ํ•œ ์ด๊ฒƒ์„ ์กฐํƒ€ ๋ชฉ๋ก์—์„œ ์–ป์Šต๋‹ˆ๋‹ค.

์ด๋ฆ„ ๋„ค์ž„์ŠคํŽ˜์ด์Šค ๊ฐœ์ •ํŒ ์—…๋ฐ์ดํŠธ ์ƒํƒœ ์ฐจํŠธ
๊ธฐ๋ณธ ์ˆ˜์‹  ๊ธฐ๋ณธ 1 2019-09-06 13:17:33.8463781 -0400 EDT ๋ฐฐํฌ nginx-ingress-1.18.0
๊ธฐ๋ณธ ์ˆ˜์‹  ๊ธฐ๋ณธ๊ฐ’ 2 2019-09-06 13:21:11.6428945 -0400 EDT ์‹คํŒจ nginx-ingress-1.18.0

๊ทธ๋ž˜์„œ ์ถœ์‹œ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.

Helm 2์—์„œ๋Š” ๊ทธ๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. helm 3์—์„œ ๋™์ž‘์ด ๋ณ€๊ฒฝ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์ธ๊ฐ€์š” ์•„๋‹ˆ๋ฉด ๋ฒ„๊ทธ์ธ๊ฐ€์š”? ์ „์ž๋ผ๋ฉด ๊ทธ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ๋ช…๋ น์„ ์–ด๋–ป๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

helm version ์ถœ๋ ฅ: version.BuildInfo{Version:"v3.0.0-beta.2", GitCommit:"26c7338408f8db593f93cd7c963ad56f67f662d4", GitTreeState:"clean", GoVersion:"go1.12.

kubectl version ๋นŒ๋“œ ์ถœ๋ ฅ: ํด๋ผ์ด์–ธํŠธ ๋ฒ„์ „: version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.0", GitCommit:"0ed33881dc4355495f623c6f22e7dd0b7632b7c0", GitTreeState:" "2018-09-27T17:05:32Z", GoVersion:"go1.10.4", ์ปดํŒŒ์ผ๋Ÿฌ:"gc", ํ”Œ๋žซํผ:"linux/amd64"}
์„œ๋ฒ„ ๋ฒ„์ „: version.Info{์ฃผ:"1", ๋ถ€:"13", GitVersion:"v1.13.10", GitCommit:"37d169313237cb4ceb2cc4bef300f2ae3053c1a2", GitTreeState:"clean", BuildDate:"20 49Z", GoVersion:"go1.11.13", ์ปดํŒŒ์ผ๋Ÿฌ:"gc", ํ”Œ๋žซํผ:"linux/amd64"}

ํด๋ผ์šฐ๋“œ ์ œ๊ณต์ž/ํ”Œ๋žซํผ(AKS, GKE, Minikube ๋“ฑ): AKS

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

helm update --install ๋ช…๋ น๊ณผ ํ•จ๊ป˜ --force ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ helm v3.0.0-rc.2๋กœ ์„œ๋น„์Šค ์œ ํ˜• ๋˜๋Š” clusterIP๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์•„๋„ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. --force ์—†์ด๋Š” ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  67 ๋Œ“๊ธ€

์ด๊ฒƒ์€ ํ˜„์žฌ kubectl๊ณผ ์œ ์‚ฌํ•œ 3๋ฐฉํ–ฅ ๋ณ‘ํ•ฉ ํŒจ์น˜ ์ „๋žต์„ ์‚ฌ์šฉํ•˜๋Š” Helm 3์˜ ์ตœ๊ทผ ๋ณ€๊ฒฝ๊ณผ ๊ด€๋ จ์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. #6124 ์ฐธ์กฐ

์ด๊ฒƒ์„ ์žฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๋‹จ๊ณ„๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ํ›Œ๋ฅญํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐ์‚ฌ ํ•ด์š”!

ํ™•์‹ ํ•˜๋Š”!

AKS ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

MC_* ๋ฆฌ์†Œ์Šค ๊ทธ๋ฃน์— ๊ณต์šฉ IP๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

ํ•ด๋‹น ๊ณต์ธ IP์˜ IP ์ฃผ์†Œ๋ฅผ $ip์— ์ €์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ ๊ธฐ๋ณธ์ ์œผ๋กœ ํ•ด๋‹น ๋ช…๋ น์„ ๋‘ ๋ฒˆ ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

./helm ์—…๊ทธ๋ ˆ์ด๋“œ --์„ค์น˜
--set rbac.create=false
--set controller.replicaCount=2
--set controller.service.loadBalancerIP=$ip
--main-ingress ์•ˆ์ •/nginx-ingress ๋Œ€๊ธฐ

์ด๋Š” https://docs.microsoft.com/en-us/azure/aks/ingress-static-ip ์—์„œ ์ˆ˜ํ–‰๋˜๋Š” ์ž‘์—…๊ณผ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค

์ฐจ์ด์ ์€ ๋‚ด๊ฐ€ helm upgrade --install ์„ ๋‘ ๋ฒˆ ์ˆ˜ํ–‰ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ ๋ชฉ์ ์€ ๋‚ด CI/CD์— ๋‹จ์ผ ๋ช…๋ น์ค„(๋ฌด์กฐ๊ฑด)์„ ํฌํ•จํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์žฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ๋” ์ž์„ธํ•œ ์ •๋ณด๊ฐ€ ํ•„์š”ํ•˜๋ฉด ์•Œ๋ ค์ฃผ์‹ญ์‹œ์˜ค.

์žฌํ˜„ํ•˜๊ธฐ์— ์ถฉ๋ถ„ํ–ˆ์Šต๋‹ˆ๊นŒ? ๋„์›€์ด๋œ๋‹ค๋ฉด bash ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ œ๊ณต ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ํ•œ ์ฃผ ๋™์•ˆ Helm Summit EU์— ์ฐธ์„ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ์•„์ง ๋‹ต๋ณ€ํ•  ์‹œ๊ฐ„์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

์•„... ๊ฑฑ์ •๋งˆ์„ธ์š”. ์ •์ƒ ํšŒ๋‹ด์„ ์ฆ๊ธฐ์‹ญ์‹œ์˜ค!

๋‚˜๋Š” ๋˜ํ•œ์ด ๋ฌธ์ œ๋ฅผ ๊ฒช๊ณ ์žˆ๋‹ค

$ helm version --short
v3.0.0-beta.3+g5cb923e

nginx-ingress ์ฐจํŠธ๋Š” ์ฒ˜์Œ ์‹คํ–‰ํ•  ๋•Œ ์ž˜ ์„ค์น˜๋˜์ง€๋งŒ ์—…๊ทธ๋ ˆ์ด๋“œ ์‹œ์—๋Š” ...

$ helm upgrade --install first-chart stable/nginx-ingress --namespace infra
client.go:357: Cannot patch Service: "first-chart-nginx-ingress-controller" (Service "first-chart-nginx-ingress-controller" is invalid: spec.clusterIP: Invalid value: "": field is immutable)
client.go:376: Use --force to force recreation of the resource
client.go:357: Cannot patch Service: "first-chart-nginx-ingress-default-backend" (Service "first-chart-nginx-ingress-default-backend" is invalid: spec.clusterIP: Invalid value: "": field is immutable)
client.go:376: Use --force to force recreation of the resource
Error: UPGRADE FAILED: Service "first-chart-nginx-ingress-controller" is invalid: spec.clusterIP: Invalid value: "": field is immutable && Service "first-chart-nginx-ingress-default-backend" is invalid: spec.clusterIP: Invalid value: "": field is immutable
$ helm ls -n infra
NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART               
first-chart     infra           1               2019-09-17 16:15:25.513997106 -0500 CDT deployed        nginx-ingress-1.20.0
first-chart     infra           2               2019-09-17 16:15:30.845249671 -0500 CDT failed          nginx-ingress-1.20.0

๋‚˜๋Š” ์ด๊ฒƒ์ด helm3๊ฐ€ ์•„๋‹ˆ๋ผ nginx-ingress ์ฐจํŠธ์˜ ๋ฌธ์ œ๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ์ฐจํŠธ๋Š” controller.service.omitClusterIP=true ๋ฐ defaultBackend.service.omitClusterIP=true ๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š๋Š” ํ•œ ํ•ญ์ƒ controller.service.clusterIP = "" ๋ฐ defaultBackend.service.clusterIP = "" ์ „๋‹ฌ์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.

์ถœ์ฒ˜ ๋งํฌ:
https://github.com/helm/charts/blob/master/stable/nginx-ingress/values.yaml#L321
https://github.com/helm/charts/blob/master/stable/nginx-ingress/templates/controller-service.yaml#L22

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

$ helm upgrade --install ingress-test stable/nginx-ingress --set controller.service.omitClusterIP=true --set defaultBackend.service.omitClusterIP=true

ํ•ด๋ดค๋Š”๋ฐ ๊ณ„์† ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋œจ๋„ค์š”

helm upgrade --install ingx stable/nginx-ingress -f ingx-values.yaml                                            ๎‚ฒ 1 โ†ต
client.go:357: Cannot patch Service: "ingx-nginx-ingress-controller" (Service "ingx-nginx-ingress-controller" is invalid: spec.clusterIP: Invalid value: "": field is immutable)
client.go:376: Use --force to force recreation of the resource
client.go:357: Cannot patch Service: "ingx-nginx-ingress-default-backend" (Service "ingx-nginx-ingress-default-backend" is invalid: spec.clusterIP: Invalid value: "": field is immutable)
client.go:376: Use --force to force recreation of the resource
Error: UPGRADE FAILED: Service "ingx-nginx-ingress-controller" is invalid: spec.clusterIP: Invalid value: "": field is immutable && Service "ingx-nginx-ingress-default-backend" is invalid: spec.clusterIP: Invalid value: "": field is immutable

ingx-values.yaml

rbac:
  create: true
controller:
  service:
    externalTrafficPolicy: Local
    omitClusterIP: true
  autoscaling:
    enabled: true
    minReplicas: 2
    maxReplicas: 100
    targetCPUUtilizationPercentage: "70"
    targetMemoryUtilizationPercentage: "70"
defaultBackend:
  service:
    omitClusterIP: true

์•„๋ž˜์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด ํ…œํ”Œ๋ฆฟ์—๋Š” clusterIP๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

helm ํ…œํ”Œ๋ฆฟ ingx stable/nginx-ingress -f ingx-values.yaml

---
# Source: nginx-ingress/templates/controller-serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress
---
# Source: nginx-ingress/templates/default-backend-serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress-backend
---
# Source: nginx-ingress/templates/clusterrole.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress
rules:
  - apiGroups:
      - ""
    resources:
      - configmaps
      - endpoints
      - nodes
      - pods
      - secrets
    verbs:
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - services
    verbs:
      - get
      - list
      - update
      - watch
  - apiGroups:
      - extensions
      - "networking.k8s.io" # k8s 1.14+
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - events
    verbs:
      - create
      - patch
  - apiGroups:
      - extensions
      - "networking.k8s.io" # k8s 1.14+
    resources:
      - ingresses/status
    verbs:
      - update
---
# Source: nginx-ingress/templates/clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ingx-nginx-ingress
subjects:
  - kind: ServiceAccount
    name: ingx-nginx-ingress
    namespace: default
---
# Source: nginx-ingress/templates/controller-role.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress
rules:
  - apiGroups:
      - ""
    resources:
      - namespaces
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - configmaps
      - pods
      - secrets
      - endpoints
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - services
    verbs:
      - get
      - list
      - update
      - watch
  - apiGroups:
      - extensions
      - "networking.k8s.io" # k8s 1.14+
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
      - "networking.k8s.io" # k8s 1.14+
    resources:
      - ingresses/status
    verbs:
      - update
  - apiGroups:
      - ""
    resources:
      - configmaps
    resourceNames:
      - ingress-controller-leader-nginx
    verbs:
      - get
      - update
  - apiGroups:
      - ""
    resources:
      - configmaps
    verbs:
      - create
  - apiGroups:
      - ""
    resources:
      - endpoints
    verbs:
      - create
      - get
      - update
  - apiGroups:
      - ""
    resources:
      - events
    verbs:
      - create
      - patch
---
# Source: nginx-ingress/templates/controller-rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: ingx-nginx-ingress
subjects:
  - kind: ServiceAccount
    name: ingx-nginx-ingress
    namespace: default
---
# Source: nginx-ingress/templates/controller-service.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    component: "controller"
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress-controller
spec:
  externalTrafficPolicy: "Local"
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: http
    - name: https
      port: 443
      protocol: TCP
      targetPort: https
  selector:
    app: nginx-ingress
    component: "controller"
    release: ingx
  type: "LoadBalancer"
---
# Source: nginx-ingress/templates/default-backend-service.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    component: "default-backend"
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress-default-backend
spec:
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: http
  selector:
    app: nginx-ingress
    component: "default-backend"
    release: ingx
  type: "ClusterIP"
---
# Source: nginx-ingress/templates/controller-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    component: "controller"
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress-controller
spec:
  replicas: 1
  revisionHistoryLimit: 10
  strategy:
    {}
  minReadySeconds: 0
  template:
    metadata:
      labels:
        app: nginx-ingress
        component: "controller"
        release: ingx
    spec:
      dnsPolicy: ClusterFirst
      containers:
        - name: nginx-ingress-controller
          image: "quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.1"
          imagePullPolicy: "IfNotPresent"
          args:
            - /nginx-ingress-controller
            - --default-backend-service=default/ingx-nginx-ingress-default-backend
            - --election-id=ingress-controller-leader
            - --ingress-class=nginx
            - --configmap=default/ingx-nginx-ingress-controller
          securityContext:
            capabilities:
                drop:
                - ALL
                add:
                - NET_BIND_SERVICE
            runAsUser: 33
            allowPrivilegeEscalation: true
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          livenessProbe:
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 1
            successThreshold: 1
            failureThreshold: 3
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
            - name: https
              containerPort: 443
              protocol: TCP
          readinessProbe:
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 1
            successThreshold: 1
            failureThreshold: 3
          resources:
            {}
      hostNetwork: false
      serviceAccountName: ingx-nginx-ingress
      terminationGracePeriodSeconds: 60
---
# Source: nginx-ingress/templates/default-backend-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    component: "default-backend"
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress-default-backend
spec:
  replicas: 1
  revisionHistoryLimit: 10
  template:
    metadata:
      labels:
        app: nginx-ingress
        component: "default-backend"
        release: ingx
    spec:
      containers:
        - name: nginx-ingress-default-backend
          image: "k8s.gcr.io/defaultbackend-amd64:1.5"
          imagePullPolicy: "IfNotPresent"
          args:
          securityContext:
            runAsUser: 65534
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
              scheme: HTTP
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 5
            successThreshold: 1
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
              scheme: HTTP
            initialDelaySeconds: 0
            periodSeconds: 5
            timeoutSeconds: 5
            successThreshold: 1
            failureThreshold: 6
          ports:
            - name: http
              containerPort: 8080
              protocol: TCP
          resources:
            {}
      serviceAccountName: ingx-nginx-ingress-backend
      terminationGracePeriodSeconds: 60
---
# Source: nginx-ingress/templates/controller-hpa.yaml
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    component: "controller"
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress-controller
spec:
  scaleTargetRef:
    apiVersion: apps/v1beta1
    kind: Deployment
    name: ingx-nginx-ingress-controller
  minReplicas: 2
  maxReplicas: 100
  metrics:
    - type: Resource
      resource:
        name: cpu
        targetAverageUtilization: 70
    - type: Resource
      resource:
        name: memory
        targetAverageUtilization: 70

์›๋ž˜ omitClusterIP ๋งค๊ฐœ๋ณ€์ˆ˜ ์—†์ด ๋ฐฐํฌํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•œ ๊ฒƒ์œผ๋กœ ์˜์‹ฌ๋˜๋ฉฐ helm v3์€ clusterIP: "" ๋œ ์›๋ž˜ ๋งค๋‹ˆํŽ˜์ŠคํŠธ์™€ 3๋ฐฉํ–ฅ ๋ณ‘ํ•ฉ์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.

helm get manifest ingx --revision 1 | grep "clusterIP"
  clusterIP: ""
  clusterIP: ""

๊ธฐ์กด ์ฐจํŠธ๋ฅผ ๋จผ์ € ์‚ญ์ œํ•˜๊ณ  omitClusterIP ์˜ต์…˜์œผ๋กœ ๋‹ค์‹œ ์ƒ์„ฑํ•˜์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฒฐ๋ก ์ ์œผ๋กœ @bambash ์—์„œ ์ œ์•ˆํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€

$ helm upgrade --install ingress-test stable/nginx-ingress --set controller.service.omitClusterIP=true --set defaultBackend.service.omitClusterIP=true

helm v3์— ๊ธฐ์กด ๋งค๋‹ˆํŽ˜์ŠคํŠธ์™€์˜ ๋ณ‘ํ•ฉ์„ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์œผ๋ฉด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋ฆด๋ฆฌ์Šค๋ฅผ ์ฒ˜์Œ ์„ค์น˜ํ•  ๋•Œ ์ด ๊ฐ’์„ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ง€์ •ํ–ˆ์–ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด ๋ฆด๋ฆฌ์Šค๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์ด ๋” ๊นŒ๋‹ค๋กœ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค...

๊ฐ’์— clusterIP๊ฐ€ ์—†๋Š” metric-server-2.8.8๊ณผ helm v3.0.0-rc.2๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋‹ค๋ฅธ ์ฐจํŠธ์—์„œ ์ด ๋ฌธ์ œ๋ฅผ ๊ฒช๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ์ถฉ๊ณ ? ์–ด๋–ป๊ฒŒ ์ง„ํ–‰ํ•ด์•ผ ํ• ์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋‚ด ๋ฌธ์ œ๋Š” helmfile v0.95.0์— ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ทธ๊ณณ์œผ๋กœ ๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค :)

helm update --install ๋ช…๋ น๊ณผ ํ•จ๊ป˜ --force ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ helm v3.0.0-rc.2๋กœ ์„œ๋น„์Šค ์œ ํ˜• ๋˜๋Š” clusterIP๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์•„๋„ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. --force ์—†์ด๋Š” ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

@johannges , ๋ฐฉ๊ธˆ ๊ฐ™์€ ๊ฒƒ์„ ๊ฒŒ์‹œํ•˜๋ ค๊ณ ํ–ˆ์Šต๋‹ˆ๋‹ค. :+1:

omitClusterIP: true ์„ค์ •์€ defaultBackend ๋ฐ ์ปจํŠธ๋กค๋Ÿฌ ์„œ๋น„์Šค์—์„œ๋Š” ์ž‘๋™ํ•˜์ง€๋งŒ ๋ฉ”ํŠธ๋ฆญ ์„œ๋น„์Šค์—์„œ๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์—…๊ทธ๋ ˆ์ด๋“œ ์ค‘ --force ์˜ต์…˜์ด ์žˆ๋Š” ์กฐํƒ€ ์žฅ์น˜์— ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
Helm์€ ์„œ๋น„์Šค๋ฅผ ์žฌ์ƒ์„ฑํ•˜๋ ค๊ณ  ํ•˜์ง€๋งŒ spec.clusterIP๋„ ๋Œ€์ฒดํ•˜๋ฏ€๋กœ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
๋‚˜๋งŒ์˜ ๋งž์ถค ์ฐจํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
Error: UPGRADE FAILED: failed to replace object: Service "litespeed" is invalid: spec.clusterIP: Invalid value: "": field is immutable

์‚ฌ์‹ค ์„œ๋น„์Šค(๋˜๋Š” ์ฐจํŠธ) ์ดˆ๊ธฐํ™”์—์„œ clusterIP ์ •์˜๋ฅผ ์ƒ๋žตํ•˜๋ฉด ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค ๐Ÿ‘

๊ธฐ์กด์— ๋ฐฐํฌ๋œ _kafka_ ๋ฐ _redis_ ์ฐจํŠธ ๋ฆด๋ฆฌ์Šค์—์„œ๋„ ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. --force ์ œ๊ฑฐํ•˜๋ฉด ์‹ค์ œ๋กœ ์ด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ _redis_ ๋ฆด๋ฆฌ์Šค์—์„œ ์ƒˆ๋กœ์šด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
Error: UPGRADE FAILED: release redis failed, and has been rolled back due to atomic being set: cannot patch "redis-master" with kind StatefulSet: StatefulSet.apps "redis-master" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'template', and 'updateStrategy' are forbidden

@bacongobbler ๋Š” ์ด๊ฒƒ์ด Helm v3 3๋ฐฉํ–ฅ ๋ณ‘ํ•ฉ ํŒจ์น˜ ์ „๋žต๊ณผ ๊ด€๋ จ์ด ์žˆ๋‹ค๋Š” ๋ฐ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ „๋žต์€ Kubernetes๊ฐ€ ์ฒ˜์Œ ์ƒ์„ฑ ํ›„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๊ฑฐ๋‚˜ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผํ•˜๋Š” ์—…๋ฐ์ดํŠธ/ํŒจ์น˜์— ํ•„๋“œ(์ด์ „๊ณผ ๋™์ผํ•œ ๊ฐ’ ํฌํ•จ)๋ฅผ ์ „๋‹ฌํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋ˆ„๊ตฐ๊ฐ€ terraform์„ ํ†ตํ•ด helm v3๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ --force ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋„๋ก ์ง์ ‘ ๋งํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— helm delete ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฐจํŠธ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์‚ญ์ œํ•œ ๋‹ค์Œ terraform์„ ๋‹ค์‹œ ์‹คํ–‰ํ•˜๋Š” ๋ฐ ์„ฑ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์งœ์ฆ๋‚˜์ง€๋งŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

ํŽธ์ง‘: ์ „์ฒด ์˜ค๋ฅ˜: ( "nginx-ingress-singleton-controller"๋Š” ๋‚ด๊ฐ€ ์„ค์ •ํ•œ ๋ฆด๋ฆฌ์Šค ์ด๋ฆ„์ž…๋‹ˆ๋‹ค. ํŠน๋ณ„ํ•œ ์˜๋ฏธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค)

Error: cannot patch "nginx-ingress-singleton-controller" with kind Service: Service "nginx-ingress-singleton-controller" is invalid: spec.clusterIP: Invalid value:
"": field is immutable && cannot patch "nginx-ingress-singleton-default-backend" with kind Service: Service "nginx-ingress-singleton-default-backend" is invalid: sp
ec.clusterIP: Invalid value: "": field is immutable

  on .terraform/modules/app_dev/nginx-ingress.tf line 1, in resource "helm_release" "nginx_ingress":
   1: resource "helm_release" "nginx_ingress" {

@zen4ever ๋Š” https://github.com/helm/helm/issues/6378#issuecomment -532766512์—์„œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ข€ ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•ด ๋ณด๋„๋ก ํ• ๊ฒŒ์š”....

๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ์ง€์ ํ–ˆ๋“ฏ์ด ์ฐจํŠธ๊ฐ€ ๋นˆ ๋ฌธ์ž์—ด๋กœ clusterIP๋ฅผ ์ •์˜ํ•  ๋•Œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์„œ๋น„์Šค๊ฐ€ ์„ค์น˜๋˜๋ฉด Kubernetes๋Š” ์„œ๋น„์Šค์— ํ• ๋‹น๋œ clusterIP๋กœ ์ด ํ•„๋“œ๋ฅผ ์ฑ„์›๋‹ˆ๋‹ค.

helm upgrade ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ์ฐจํŠธ์—์„œ clusterIP ๋ฅผ ์ œ๊ฑฐํ•˜๋„๋ก ์š”์ฒญํ•˜๋ฏ€๋กœ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ spec.clusterIP: Invalid value: "": field is immutable ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

์ด๊ฒƒ์€ ๋‹ค์Œ ๋™์ž‘์œผ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

  1. ์„ค์น˜ ์‹œ ์ฐจํŠธ๋Š” clusterIP ๊ฐ€ ๋นˆ ๋ฌธ์ž์—ด์ด ๋˜๊ธฐ๋ฅผ ์›ํ•œ๋‹ค๊ณ  ์ง€์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  2. Kubernetes๋Š” clusterIP ์„œ๋น„์Šค๋ฅผ ์ž๋™ ํ• ๋‹นํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ์˜ˆ์—์„œ๋Š” 172.17.0.1 ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  3. helm upgrade ์—์„œ ์ฐจํŠธ๋Š” clusterIP ๊ฐ€ ๋นˆ ๋ฌธ์ž์—ด์ด ๋˜๊ธฐ๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค (๋˜๋Š” ์œ„์˜ ๊ฒฝ์šฐ ์ƒ๋žต๋จ).

3๋ฐฉํ–ฅ ํŒจ์น˜๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์ด์ „ ์ƒํƒœ๋Š” "" , ๋ผ์ด๋ธŒ ์ƒํƒœ๋Š” ํ˜„์žฌ "172.17.0.1" , ์ œ์•ˆ๋œ ์ƒํƒœ๋Š” "" ์ž„์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. Helm์€ ์‚ฌ์šฉ์ž๊ฐ€ clusterIP ๋ฅผ "172.17.0.1"์—์„œ ""๋กœ ๋ณ€๊ฒฝํ•˜๋„๋ก ์š”์ฒญํ•œ ๊ฒƒ์„ ๊ฐ์ง€ํ•˜์—ฌ ํŒจ์น˜๋ฅผ ์ œ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค.

Helm 2์—์„œ๋Š” ๋ผ์ด๋ธŒ ์ƒํƒœ๋ฅผ ๋ฌด์‹œํ•˜๋ฏ€๋กœ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์—†์—ˆ๊ณ (์ด์ „ ์ƒํƒœ: clusterIP: "" ์—์„œ ์ƒˆ ์ƒํƒœ๋กœ: clusterIP: "" ) ์ด ๋™์ž‘์„ ์šฐํšŒํ•˜์—ฌ ํŒจ์น˜๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋‚ด ๊ถŒ์žฅ ์‚ฌํ•ญ์€ ํ…œํ”Œ๋ฆฟ ์ถœ๋ ฅ์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. clusterIP ๊ฐ€ ๊ฐ’์œผ๋กœ ์ œ๊ณต๋˜์ง€ ์•Š์œผ๋ฉด ๊ฐ’์„ ๋นˆ ๋ฌธ์ž์—ด๋กœ ์„ค์ •ํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค... ํ•„๋“œ๋ฅผ ์™„์ „ํžˆ ์ƒ๋žตํ•˜์‹ญ์‹œ์˜ค.

์˜ˆ: stable/nginx-ingress :

spec:
{{- if not .Values.controller.service.omitClusterIP }}
  clusterIP: "{{ .Values.controller.service.clusterIP }}"
{{- end }}

๋‹ค์Œ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

spec:
{{- if not .Values.controller.service.omitClusterIP }}
  {{ with .Values.controller.service.clusterIP }}clusterIP: {{ quote . }}{{ end }}
{{- end }}

์ด๊ฒƒ์ด ์ด ๊ฒฝ์šฐ --set controller.service.omitClusterIP=true ์ž‘๋™ํ•˜๋Š” ์ด์œ ์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

TL;DR์€ ์„œ๋น„์Šค ํ…œํ”Œ๋ฆฟ์—์„œ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค.

clusterIP: ""

๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด Helm์€ ์„œ๋น„์Šค์˜ clusterIP๋ฅผ ์ž๋™ ์ƒ์„ฑ๋œ IP ์ฃผ์†Œ์—์„œ ๋นˆ ๋ฌธ์ž์—ด๋กœ ๋ณ€๊ฒฝํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๋ฏ€๋กœ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

๋„์›€์ด ๋˜์—ˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค!

์ด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜๋Š” ๋™์•ˆ ์ด ๊ธฐ๋Šฅ์„ ์ž‘๋™์‹œํ‚ค๋ ค๋Š” ๊ฒฝ์šฐ ์ž„์‹œ ํ•ด๊ฒฐ์ฑ…์œผ๋กœ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ์—…๋ฐ์ดํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค.

  1. ๋‹ค์Œ์„ ํ†ตํ•ด ์ปจํŠธ๋กค๋Ÿฌ ๋ฐ ๊ธฐ๋ณธ ๋ฐฑ์—”๋“œ์— ๋Œ€ํ•œ clusterIP ๊ฐ’์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
    kubectl get svc | grep ingress
  2. ๊ธฐ์กด helm ๊ฐ’ ํŒŒ์ผ์— ๋‹ค์Œ ๋ฎ์–ด์“ฐ๊ธฐ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    controller: service: clusterIP: <cluster-ip-address-for-controller> defaultBackend: service: clusterIP: <cluster-ip-address-for-default-backend>
  3. ์—…๋ฐ์ดํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์‹คํ–‰ ์ค‘์ธ ํด๋Ÿฌ์Šคํ„ฐ์— ๋Œ€ํ•ด ์ด๊ฒƒ์„ ํ…Œ์ŠคํŠธํ–ˆ์œผ๋ฉฐ ์žฌ์ƒ์„ฑ์ด ํ•„์š”ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ๋„ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ์ข‹์€ ์ „ํ™” @treacher. --set ๋˜๋Š” ๊ฐ’ ํŒŒ์ผ์„ ํ†ตํ•ด ๋™์ผํ•œ ๊ฐ’์„ ์„ค์ •ํ•˜๋ฉด ์—…๊ทธ๋ ˆ์ด๋“œ๊ฐ€ clusterIP ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํŒจ์น˜๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์œ„์—์„œ ์„ค๋ช…ํ•œ 3๋ฐฉํ–ฅ ๋ณ‘ํ•ฉ ํŒจ์น˜ ๋™์ž‘์— ๋”ฐ๋ผ ์˜๋„์ ์œผ๋กœ ์ž‘๋™ํ•˜์—ฌ ๋‹ซ์Šต๋‹ˆ๋‹ค. ์กฐ์น˜ ํ•ญ๋ชฉ์€ ์ด ์ฐจํŠธ๊ฐ€ https://github.com/helm/helm/issues/6378#issuecomment-557746499์— ์ œ๊ณต๋œ ๊ถŒ์žฅ ์‚ฌํ•ญ์„ ๋”ฐ๋ฅด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. Helm์˜ ๋์—์„œ ํ•  ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค. :)

https://github.com/helm/charts/pull/19146/files ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค! @bacongobbler๋‹˜ ๊ฐ์‚ฌ

@zen4ever ๋Š” # 6378 (๋Œ“๊ธ€) ์—์„œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ข€ ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•ด ๋ณด๋„๋ก ํ• ๊ฒŒ์š”....

๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ์ง€์ ํ–ˆ๋“ฏ์ด ์ฐจํŠธ๊ฐ€ ๋นˆ ๋ฌธ์ž์—ด๋กœ clusterIP๋ฅผ ์ •์˜ํ•  ๋•Œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์„œ๋น„์Šค๊ฐ€ ์„ค์น˜๋˜๋ฉด Kubernetes๋Š” ์„œ๋น„์Šค์— ํ• ๋‹น๋œ clusterIP๋กœ ์ด ํ•„๋“œ๋ฅผ ์ฑ„์›๋‹ˆ๋‹ค.

helm upgrade ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ์ฐจํŠธ์—์„œ clusterIP ๋ฅผ ์ œ๊ฑฐํ•˜๋„๋ก ์š”์ฒญํ•˜๋ฏ€๋กœ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ spec.clusterIP: Invalid value: "": field is immutable ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

์ด๊ฒƒ์€ ๋‹ค์Œ ๋™์ž‘์œผ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

  1. ์„ค์น˜ ์‹œ ์ฐจํŠธ๋Š” clusterIP ๊ฐ€ ๋นˆ ๋ฌธ์ž์—ด์ด ๋˜๊ธฐ๋ฅผ ์›ํ•œ๋‹ค๊ณ  ์ง€์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  2. Kubernetes๋Š” clusterIP ์„œ๋น„์Šค๋ฅผ ์ž๋™ ํ• ๋‹นํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ์˜ˆ์—์„œ๋Š” 172.17.0.1 ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  3. helm upgrade ์—์„œ ์ฐจํŠธ๋Š” clusterIP ๊ฐ€ ๋นˆ ๋ฌธ์ž์—ด์ด ๋˜๊ธฐ๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค (๋˜๋Š” ์œ„์˜ ๊ฒฝ์šฐ ์ƒ๋žต๋จ).

3๋ฐฉํ–ฅ ํŒจ์น˜๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์ด์ „ ์ƒํƒœ๋Š” "" , ๋ผ์ด๋ธŒ ์ƒํƒœ๋Š” ํ˜„์žฌ "172.17.0.1" , ์ œ์•ˆ๋œ ์ƒํƒœ๋Š” "" ์ž„์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. Helm์€ ์‚ฌ์šฉ์ž๊ฐ€ clusterIP ๋ฅผ "172.17.0.1"์—์„œ ""๋กœ ๋ณ€๊ฒฝํ•˜๋„๋ก ์š”์ฒญํ•œ ๊ฒƒ์„ ๊ฐ์ง€ํ•˜์—ฌ ํŒจ์น˜๋ฅผ ์ œ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค.

Helm 2์—์„œ๋Š” ๋ผ์ด๋ธŒ ์ƒํƒœ๋ฅผ ๋ฌด์‹œํ•˜๋ฏ€๋กœ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์—†์—ˆ๊ณ (์ด์ „ ์ƒํƒœ: clusterIP: "" ์—์„œ ์ƒˆ ์ƒํƒœ๋กœ: clusterIP: "" ) ์ด ๋™์ž‘์„ ์šฐํšŒํ•˜์—ฌ ํŒจ์น˜๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋‚ด ๊ถŒ์žฅ ์‚ฌํ•ญ์€ ํ…œํ”Œ๋ฆฟ ์ถœ๋ ฅ์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. clusterIP ๊ฐ€ ๊ฐ’์œผ๋กœ ์ œ๊ณต๋˜์ง€ ์•Š์œผ๋ฉด ๊ฐ’์„ ๋นˆ ๋ฌธ์ž์—ด๋กœ ์„ค์ •ํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค... ํ•„๋“œ๋ฅผ ์™„์ „ํžˆ ์ƒ๋žตํ•˜์‹ญ์‹œ์˜ค.

์˜ˆ: stable/nginx-ingress :

spec:
{{- if not .Values.controller.service.omitClusterIP }}
  clusterIP: "{{ .Values.controller.service.clusterIP }}"
{{- end }}

๋‹ค์Œ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

spec:
{{- if not .Values.controller.service.omitClusterIP }}
  {{ with .Values.controller.service.clusterIP }}clusterIP: {{ quote . }}{{ end }}
{{- end }}

์•ˆ๋…•ํ•˜์„ธ์š” @bacongobbler๋‹˜ , ๊ฐ’์ด ์ œ๊ณต๋˜์ง€ ์•Š์œผ๋ฉด clusterIP: "" clusterIP: "" ๋งˆ๋ฌด๋ฆฌ๋  ๊ฒƒ์ด๋ฏ€๋กœ clusterIP: "" ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉด ๊ฐ’ ํŒŒ์ผ์— clusterIP ์ฃผ์†Œ๋ฅผ ํ•˜๋“œ์ฝ”๋”ฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ์ด ๊ฒฝ์šฐ --set controller.service.omitClusterIP=true ์ž‘๋™ํ•˜๋Š” ์ด์œ ์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

TL;DR์€ ์„œ๋น„์Šค ํ…œํ”Œ๋ฆฟ์—์„œ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค.

clusterIP: ""

๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด Helm์€ ์„œ๋น„์Šค์˜ clusterIP๋ฅผ ์ž๋™ ์ƒ์„ฑ๋œ IP ์ฃผ์†Œ์—์„œ ๋นˆ ๋ฌธ์ž์—ด๋กœ ๋ณ€๊ฒฝํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๋ฏ€๋กœ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

๋„์›€์ด ๋˜์—ˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค!

@bacongobbler๋‹˜ , helm v2 ๋ฆด๋ฆฌ์Šค๋ฅผ helm v3์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋Š” ๋™์•ˆ ๋™์ผํ•œ ๋ฌธ์ œ์— ์ง๋ฉดํ–ˆ์Šต๋‹ˆ๋‹ค. ์„œ๋น„์Šค์—์„œ type: ClusterIP ๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ ClusterIP ๋ฅผ ์ „ํ˜€ ์ƒ๋žตํ•˜๋ฉด ๋‹ค์Œ์„ ์–ป์Šต๋‹ˆ๋‹ค.

Error: UPGRADE FAILED: failed to replace object: Service "dummy" is invalid: spec.clusterIP: Invalid value: "": field is immutable

helm ํ…œํ”Œ๋ฆฟ์— spec.clusterIP: ๊ฐ€ ์—†์ง€๋งŒ helm 2to3์„ ํ†ตํ•ด ๋ฆด๋ฆฌ์Šค๋ฅผ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•œ ํ›„ ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

์„œ๋น„์Šค ํ…œํ”Œ๋ฆฟ:

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}
  labels:
    app: {{ .Values.image.name }}
    chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "-" }}
    cluster: {{ default "unknown" .Values.cluster }}
    region: {{ default "unknown" .Values.region }}
    datacenter: {{ default "unknown" .Values.datacenter }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  type: ClusterIP
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.port }}
      protocol: TCP
      name: http
  selector:
    app: {{ .Values.image.name }}
    release: {{ .Release.Name }}

๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌธ์ œ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์„œ๋น„์Šค๋ฅผ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์•˜๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์—…๊ทธ๋ ˆ์ด๋“œ ์ „ ๋ณ€๊ฒฝ๋œ ์ธ๊ทธ๋ ˆ์Šค์ž…๋‹ˆ๋‹ค.

helm upgrade --install ์—์„œ --force ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ญ์ œํ•˜๊ณ  ๋ถˆ๋ณ€ ํ•„๋“œ๋ฅผ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์œผ๋ฉด ๋ชจ๋“  ๊ฒƒ์ด ์ž˜ ์ž‘๋™ํ•˜๋Š” ๋ถˆ๋ณ€ ํ•„๋“œ๊ฐ€ ์žˆ๋Š” ๋ฆฌ์†Œ์Šค์— ์˜ํ–ฅ์„ ์ค๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ž์›์˜ apiversion์„ ์ถฉ๋Œ์‹œํ‚ค๊ณ  ์‹ถ๋‹ค๋ฉด??? ๋ฆฌ์†Œ์Šค๋ฅผ ๋‹ค์‹œ ์ƒ์„ฑํ•ด์•ผ ํ•˜์ง€๋งŒ helm 3๋Š” ๋ฆฌ์†Œ์Šค๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค....
@bacongobbler ^^

helm 3์˜ ์ƒˆ๋กœ์šด apiVersion์œผ๋กœ ์—…๋ฐ์ดํŠธ hpa๋ฅผ ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค.
Error: UPGRADE FAILED: rendered manifests contain a new resource that already exists. Unable to continue with update: existing resource conflict: kind: HorizontalPodAutoscaler, namespace: stage, name: dummy-stage

@bacongobbler ๋ฐ @kritcher722 ์ถ”์ฒœ ์•ˆํ•จ ๋Œ“๊ธ€์ด ์—…๋ฐ์ดํŠธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์—ฌ๋Ÿฌ๋ถ„์ด ์ถ”์ฒœ์„ ์‚ญ์ œํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ์—ฌ์ „ํžˆ ์˜๊ฒฌ์ด ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ๋ Œ๋”๋ง๋œ ๋งค๋‹ˆํŽ˜์ŠคํŠธ์— clusterIP: "" ๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฒƒ์ด ์ข‹์€ ์ด์œ ์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์„ค๋ช…ํ•ด ์ฃผ์‹ญ์‹œ์˜ค.

Microsoft๊ฐ€ ํ”„๋กœ์ ํŠธ์˜ ๋ฉ˜ํ† ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์Šคํƒ€์ผ์ด ๋ณด์ž…๋‹ˆ๋‹ค. :)

๋‹ค์‹œ ์—ด์–ด์ฃผ์„ธ์š”. ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. nasseemkullah๊ฐ€ ์ œ์•ˆํ•œ ์ด "ํ•ดํ‚น"์€ ์ ์ ˆํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ๋จธ๋ฆฌ ์œ„๋กœ ๋›ฐ์–ด์˜ค๋ผ๊ณ  ์š”๊ตฌํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค. ๊ทธ๋ƒฅ ๊ณ ์ณ. ๋งค์šฐ ์—ด์•…ํ•œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฒฝ๋กœ. ํ—ฌ๋ฆ„์€ ์งœ์ฆ๋‚œ๋‹ค.

@antonakv ํ•œ ํ•ด๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๋ฐฉ๋ฒ• :)
๋‚˜๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์ฐจํŠธ์—์„œ ๊ตฌ์„ฑ ๊ฐ€๋Šฅํ•œ ๊ฐ’์œผ๋กœ clusterIP๋ฅผ ์ œ๊ณตํ•  ๋•Œ ์šฐ๋ฆฌ๊ฐ€ ๋ถˆ์žฅ๋‚œ์„ ํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉฐ, ํŠนํžˆ ํ•˜๋‚˜์˜ ๋„๊ตฌ/์‚ฌ๋žŒ/PR์„ ์ „์ ์œผ๋กœ ๋น„๋‚œํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.
clusterIP๊ฐ€ ๊ตฌ์„ฑ ๊ฐ€๋Šฅํ•œ ๊ฐ’์ด์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ Œ๋”๋ง๋œ ํ…œํ”Œ๋ฆฟ์— ์—†์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” https://github.com/helm/charts/blob/270172836fd8cf56d787cf7d04d938856de0c794/stable ์— ๋”ฐ๋ผ ๊ฐ’ ํŒŒ์ผ์—์„œ ์ฃผ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์•„์ด๋””์–ด์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ๋‚ด๊ฐ€ ํ‹€๋ฆฌ์ง€ ์•Š์•˜๋‹ค๋ฉด ๊ทธ ๋ณ€๊ฒฝ ์‹œ์ ์— ์ฐจํŠธ๋ฅผ ์„ค์น˜ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ๋ฏธ๋ž˜์˜ ๊ณจ์นซ๊ฑฐ๋ฆฌ๋ฅผ ์˜ˆ๋ฐฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ์˜ ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•ด ์‚ฌ์ „์„ ์„ค์น˜ํ•˜๊ณ  helm3๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ•œ ์‚ฌ๋žŒ (์ž์‹  ํฌํ•จ), ๋‚˜๋Š” ์œ ์ผํ•œ ์˜ต์…˜ I๋Š” ์šฐ๋ฆฌ์˜ ๊ฐ€์น˜ ํŒŒ์ผ์˜ ํ˜„์žฌ clusterIP ๊ฐ’์„ hardcording ๋˜๋Š” ์ œ๊ฑฐํ•˜๊ณ  ์ฐจํŠธ๋ฅผ (์›์ธ ๊ฐ€๋™ ์ค‘๋‹จ!) ๋‹ค์‹œ ์„ค์น˜ ๋‘๋ ค์›Œ ๋ณด๋‹ค.

์˜๊ฒฌ์€ ์ œ ์˜๊ฒฌ์ž…๋‹ˆ๋‹ค. ์ €๋Š” ์กฐํƒ€ ์ž‘์—…์„ ์œ„ํ•ด ๋ˆ์„ ๋ฐ›๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋‹น์‹ ๊ณผ ๊ฐ™์€ ์ตœ์ข… ์‚ฌ์šฉ์ž์ผ ๋ฟ์ž…๋‹ˆ๋‹ค. ์ด ํ’€ํƒ€์ž„์œผ๋กœ ์ผํ•˜๊ธฐ ์œ„ํ•ด ๊ธ‰์—ฌ๋ฅผ ๋ฐ›๋Š” ์‚ฌ๋žŒ๋“ค์€ ๋” ๋งŽ์€ ํ†ต์ฐฐ๋ ฅ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ƒˆํ•ด ๋ณต ๋งŽ์ด ๋ฐ›์œผ์‹œ๊ณ  ํ–‰์šด์„ ๋น•๋‹ˆ๋‹ค! ์ฃผ๋„๊ถŒ์„ ํฌ๊ธฐํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค. ํ•จ๊ป˜๋ผ๋ฉด ๋” ๋‚˜์•„์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@bacongobbler๋‹˜ , helm v2 ๋ฆด๋ฆฌ์Šค๋ฅผ helm v3์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋Š” ๋™์•ˆ ๋™์ผํ•œ ๋ฌธ์ œ์— ์ง๋ฉดํ–ˆ์Šต๋‹ˆ๋‹ค. ์„œ๋น„์Šค์—์„œ type: ClusterIP ๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ ClusterIP ๋ฅผ ์ „ํ˜€ ์ƒ๋žตํ•˜๋ฉด ๋‹ค์Œ์„ ์–ป์Šต๋‹ˆ๋‹ค.

Error: UPGRADE FAILED: failed to replace object: Service "dummy" is invalid: spec.clusterIP: Invalid value: "": field is immutable

helm ํ…œํ”Œ๋ฆฟ์— spec.clusterIP: ๊ฐ€ ์—†์ง€๋งŒ helm 2to3์„ ํ†ตํ•ด ๋ฆด๋ฆฌ์Šค๋ฅผ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•œ ํ›„ ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

์„œ๋น„์Šค ํ…œํ”Œ๋ฆฟ:

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}
  labels:
    app: {{ .Values.image.name }}
    chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "-" }}
    cluster: {{ default "unknown" .Values.cluster }}
    region: {{ default "unknown" .Values.region }}
    datacenter: {{ default "unknown" .Values.datacenter }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  type: ClusterIP
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.port }}
      protocol: TCP
      name: http
  selector:
    app: {{ .Values.image.name }}
    release: {{ .Release.Name }}

๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ฐจํŠธ์—์„œ clusterIP ๋ฅผ ์ „ํ˜€ ์ •์˜ํ•˜์ง€ ์•Š์œผ๋ฉฐ ์ตœ์ข… ํ…œํ”Œ๋ฆฟ์—๋Š” ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์—ฌ์ „ํžˆ ๋™์ผํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉฐ --force ํ”Œ๋ž˜๊ทธ๋งŒ ์žˆ์Šต๋‹ˆ๋‹ค.

๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

apiVersion: v1
kind: Service
{{ include "mde.metadata" $ }}
spec:
  ports:
  - name: {{ include "mde.portName" $ | quote }}
    port: {{ include "mde.port" $ }}
    protocol: TCP
    targetPort: {{ include "mde.port" $ }}
  selector:
    app: {{ include "mde.name" $ }}
  sessionAffinity: None
  type: ClusterIP

spec.clusterIP ๋Š” ์„œ๋น„์Šค ํ…œํ”Œ๋ฆฟ์˜ ์ผ๋ถ€๊ฐ€ ์•„๋‹ˆ์ง€๋งŒ ์•„์ง Helm 3.0.2 ๋ฐ helm upgrade ... --force --install ํ˜ธ์ถœ์„ ํ†ตํ•ด ๋‹ค์Œ๋„ ๋ณด๊ณ ๋ฉ๋‹ˆ๋‹ค.

์˜ค๋ฅ˜: ์—…๊ทธ๋ ˆ์ด๋“œ ์‹คํŒจ: ๊ฐœ์ฒด ๊ต์ฒด ์‹คํŒจ: ์„œ๋น„์Šค "๋”๋ฏธ"๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์Œ: spec.clusterIP: ์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฐ’: "": ํ•„๋“œ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋‹ค์‹œ ์—ด์–ด์ฃผ์„ธ์š”.

@tomcruise81 --force ์— ๋Œ€ํ•œ ์Šค๋ ˆ๋“œ๋Š” https://github.com/helm/helm/issues/7350 ์„ ์ฐธ์กฐ kubectl replace ๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์„œ๋น„์Šค ํด๋Ÿฌ์Šคํ„ฐ IP ๋ฐ 3๋ฐฉํ–ฅ ๋ณ‘ํ•ฉ ํŒจ์น˜ ์ „๋žต( --force ํ”Œ๋ž˜๊ทธ๊ฐ€ ์—†๋Š” helm upgrade ๊ณผ ๊ด€๋ จ๋œ ์—ฌ๊ธฐ์— ์„ค๋ช…๋œ ๊ฒƒ๊ณผ๋Š” ๋ณ„๊ฐœ์˜ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

@bacongobbler - ๋น ๋ฅธ ์‘๋‹ต๊ณผ ์„ค๋ช…์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ๋ณด๊ณ :

https://github.com/helm/helm/blob/a963736f6675e972448bf7a5fd141628fd0ae4df/pkg/kube/client.go#L405 -L411

https://github.com/kubernetes/cli-runtime/blob/master/pkg/resource/helper.go#L155 -L181์„ ์‚ฌ์šฉํ•˜๋ฉด helper.Replace ๋Œ€ํ•œ ํ˜ธ์ถœ์ด kubectl replace -f ... --force ์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค(๋งˆ์ง€๋ง‰์— --force ์ฐธ๊ณ ).

์—ฌ๊ธฐ์„œ ๋งŽ์€ ํ˜ผ๋ž€์ด ์ผ์–ด๋‚˜๋Š” ๊ฒƒ ๊ฐ™์•„์š”.

๋‚˜๋Š” helm upgrade ... --force ๋Œ€ํ•œ ๋‚˜์˜ ๊ธฐ๋Œ€์น˜๋ฅผ ์•Œ๊ณ  ์žˆ์œผ๋ฉฐ ๋Œ€์ฒด ์ „๋žต์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์€ kubectl replace -f ... --force ์™€ ๊ฐ™์€ ์ผ์„ ํ•  ๊ฒƒ์ด๋ผ๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

@bacongobbler๋‹˜ , helm v2 ๋ฆด๋ฆฌ์Šค๋ฅผ helm v3์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋Š” ๋™์•ˆ ๋™์ผํ•œ ๋ฌธ์ œ์— ์ง๋ฉดํ–ˆ์Šต๋‹ˆ๋‹ค. ์„œ๋น„์Šค์—์„œ type: ClusterIP ๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ ClusterIP ๋ฅผ ์ „ํ˜€ ์ƒ๋žตํ•˜๋ฉด ๋‹ค์Œ์„ ์–ป์Šต๋‹ˆ๋‹ค.
Error: UPGRADE FAILED: failed to replace object: Service "dummy" is invalid: spec.clusterIP: Invalid value: "": field is immutable
helm ํ…œํ”Œ๋ฆฟ์— spec.clusterIP: ๊ฐ€ ์—†์ง€๋งŒ helm 2to3์„ ํ†ตํ•ด ๋ฆด๋ฆฌ์Šค๋ฅผ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•œ ํ›„ ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
์„œ๋น„์Šค ํ…œํ”Œ๋ฆฟ:

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}
  labels:
    app: {{ .Values.image.name }}
    chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "-" }}
    cluster: {{ default "unknown" .Values.cluster }}
    region: {{ default "unknown" .Values.region }}
    datacenter: {{ default "unknown" .Values.datacenter }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  type: ClusterIP
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.port }}
      protocol: TCP
      name: http
  selector:
    app: {{ .Values.image.name }}
    release: {{ .Release.Name }}

๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ฐจํŠธ์—์„œ clusterIP ๋ฅผ ์ „ํ˜€ ์ •์˜ํ•˜์ง€ ์•Š์œผ๋ฉฐ ์ตœ์ข… ํ…œํ”Œ๋ฆฟ์—๋Š” ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์—ฌ์ „ํžˆ ๋™์ผํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉฐ --force ํ”Œ๋ž˜๊ทธ๋งŒ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๋ฆด๋ฆฌ์Šค ๋งค๋‹ˆํŽ˜์ŠคํŠธ์— clusterIP ๊ฐ€ ์—†๋Š”์ง€ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

$ helm get manifest paywall-api-ee | grep clusterIP
$

์—ฌ๊ธฐ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ClusterIP ์–ด๋””์—๋„ ์ •์˜ํ•˜์ง€ ์•Š์ง€๋งŒ ์—ฌ์ „ํžˆ ์˜ค๋ฅ˜๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์œผ๋กœ ์ข€ ๋” ์žฅ๋‚œ์„ ์น˜๋ฉด์„œ ๋‚˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์‚ฌ์‹ค์„ ๊ด€์ฐฐํ–ˆ์Šต๋‹ˆ๋‹ค.

  • helm upgrade ... --force --install - ๊ฒฐ๊ณผ _์„œ๋น„์Šค "๋”๋ฏธ"๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์Œ ํ•˜์ง€ ์•Š์€ ๊ฐ’: "": ํ•„๋“œ๊ฐ€ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅ_
  • helm template ... | kubectl apply -f - - ์ž‘๋™
  • helm template ... | kubectl replace -f - - ๊ฒฐ๊ณผ _์„œ๋น„์Šค "๋”๋ฏธ"๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์Œ ํ•˜์ง€ ์•Š์€ ๊ฐ’: "": ํ•„๋“œ๊ฐ€ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅ_
  • helm template ... | kubectl replace --force -f - - ์ž‘๋™

kubectl ๋ฒ„์ „ - 1.14.6
์กฐํƒ€ ๋ฒ„์ „ - 3.0.2

@tomcruise81 helm ํ”Œ๋Ÿฌ๊ทธ์ธ 2to3์„ ์‚ฌ์šฉํ•ด ๋ณด๊ณ  helm2์—์„œ helm3 ๋ฆด๋ฆฌ์Šค๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๊ณ  ์ด์ „์— ์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ --force ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๊ฒƒ์€ ์šฐ๋ฆฌ๋ฅผ ์œ„ํ•œ ์ผ์ž…๋‹ˆ๋‹ค.
๋‚˜์— ๊ด€ํ•ด์„œ๋Š” ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ฒ˜๋Ÿผ --force ์— ์ž˜๋ชป๋œ ํ–‰๋™์ด ์žˆ์œผ๋ฉฐ ์ด ๊ฒฝ์šฐ๋Š” ๋ถˆ๋ณ€ ํ•„๋“œ๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

@alexandrsemak - ์ถ”์ฒœ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ œ ๊ฒฝ์šฐ์—๋Š” helm 3์„ ์‚ฌ์šฉํ•˜์—ฌ ์„ค์น˜ํ•˜๊ฑฐ๋‚˜ ์—…๊ทธ๋ ˆ์ด๋“œํ•œ ์ฐจํŠธ์—์„œ๋งŒ ์ด๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜์™€ ๊ฐ™์€ ๋ฌธ์ œ! ์‚ฌ์šฉ

$ helm install my-release xxxxx
$ helm upgrade --install --force my-release xxxxx

์ œ ๊ฒฝ์šฐ์—๋Š” ์ฐจํŠธ์— ์‚ฌ์šฉ๋œ ์„œ๋น„์Šค์— ๋Œ€ํ•ด ClusterIP ๋ฅผ ์ •์˜ํ•˜์ง€ ์•Š์ง€๋งŒ ๋™์ผํ•œ ๋ฌธ์ œ์— ์ง๋ฉดํ•ฉ๋‹ˆ๋‹ค(์•„๋ž˜ ์‚ฌ์–‘ ์ฐธ์กฐ).

spec:
  type: {{ .Values.service.type }}
  {{- if and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerIP)) }}
  loadBalancerIP: {{ .Values.service.loadBalancerIP }}
  {{- end }}
  ports:
    - name: htttp-XXX
      port: {{ .Values.service.port }}
      targetPort: XXX
      {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePort)) }}
      nodePort: {{ .Values.service.nodePort }}
      {{- else if eq .Values.service.type "ClusterIP" }}
      nodePort: null
      {{- end }}
  selector:
    app.kubernetes.io/name: XXX
    app.kubernetes.io/instance: {{ .Release.Name }}

๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๊ฐ€ ์ด์ „์— ๋งํ–ˆ๋“ฏ์ด Kubernetes๋Š” ์ฒ˜์Œ์œผ๋กœ ์„œ๋น„์Šค์— ํด๋Ÿฌ์Šคํ„ฐ IP๋ฅผ ํ• ๋‹นํ•˜๊ณ (์˜ˆ: clusterIP: 10.96.26.65 ) ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋ ค๊ณ  ํ•  ๋•Œ clusterIP: "" ์™€ ์ถฉ๋Œํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‚ด ํ…œํ”Œ๋ฆฟ์—์„œ ์ด๊ฒƒ์„ ์ƒ์„ฑํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์— ์œ ์˜ํ•˜์‹ญ์‹œ์˜ค: clusterIP: ""

@bacongobbler๋ฅผ ๋‹ค์‹œ ์—ด์–ด์ฃผ์„ธ์š”

๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

@juan131 @Ronsevet : ์ œ๊ฑฐ --force ์˜๋ฏธ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ง€์ • ์ฐจํŠธ์—์„œ ๋™์ผํ•œ ๋ฌธ์ œ์— ์ง๋ฉดํ•ด ์žˆ์Šต๋‹ˆ๋‹ค.
์šฐ๋ฆฌ๋Š” ์–ด๋””์—๋„ clusterip์„ ์ •์˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
ํˆฌ๊ตฌ v3.0.2
kubectl 1.14.8

๋ฌธ์ œ๋Š” ํฌ๋“œ๊ฐ€ ์ƒ์„ฑ๋˜์–ด ์‹คํ–‰ ์ค‘์ด๋”๋ผ๋„ ์ฐจํŠธ๊ฐ€ ์‹คํŒจํ•œ ์ƒํƒœ๋กœ ์œ ์ง€๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋™์ผํ•œ ๋ฆด๋ฆฌ์Šค๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋ ค๊ณ  ํ•˜๋ฉด ๊ฐ•์ œ๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
ํฌ๋“œ๊ฐ€ ์‹คํ–‰ ์ค‘์ด๋ฏ€๋กœ ๋ฆด๋ฆฌ์Šค๋ฅผ ์‚ญ์ œํ•˜๊ณ  ๋‹ค์‹œ ๋งŒ๋“ค ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
"ํž˜"์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค

๋‚˜์—๊ฒŒ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ๋ฐฉ๊ธˆ ์„œ๋น„์Šค์— ๋ ˆ์ด๋ธ”์„ ์ถ”๊ฐ€ํ–ˆ๋Š”๋ฐ ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๋‚˜๋Š” ์–ด๋””์—๋„ ClusterIP๋ฅผ ์ •์˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค - ๋ฌธ์ œ๋ฅผ ๋‹ค์‹œ ์—ฌ์‹ญ์‹œ์˜ค

@bacongobbler ์ฐจํŠธ์˜ ์ผ๋ถ€๋กœ StorageClass ๋ฅผ ๋ฐฐํฌํ•˜๊ณ  ์žˆ์œผ๋ฉฐ StorageClass์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋‹ค์Œ ๋ฆด๋ฆฌ์Šค์—์„œ๋Š” ์ผ๋ถ€ StorageClass ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ์—…๋ฐ์ดํŠธํ•  ๋•Œ helm upgrade --force ๋„ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.
StorageClasses ์—…๋ฐ์ดํŠธ์— ๋Œ€ํ•ด ์ด ๊ฒฝ์šฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ์ œ์•ˆ?

Error: UPGRADE FAILED: failed to replace object: StorageClass.storage.k8s.io "ibmc-s3fs-standard-cross-region" is invalid: parameters: Forbidden: updates to parameters are forbidden.

helm v2์—์„œ๋Š” helm upgrade --force StorageClass๋ฅผ ๊ฐ•์ œ๋กœ ์‚ญ์ œํ•˜๊ณ  ๋‹ค์‹œ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ ๋˜๋ฏ€๋กœ ์ œ๋Œ€๋กœ ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค.

https://github.com/helm/helm/helm/issues/6378#issuecomment -557746499์— ์ œ๊ณต๋œ ์„ค๋ช…์˜ ๊ฒฐ๊ณผ๊ฐ€ ์•„๋‹Œ ์ฆ์ƒ์„ ๊ฒช๊ณ  ์žˆ๋Š” ์‚ฌ๋žŒ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋ฐœ๊ฒฌํ•œ ๋‚ด์šฉ๊ณผ ๋ฌธ์ œ๋ฅผ ์žฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ์ƒˆ โ€‹โ€‹๋ฌธ์ œ๋ฅผ ์—ด์–ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ์šฐ๋ฆฌ ์ชฝ์ด์•ผ?

OP์—์„œ ์ œ๊ธฐํ•œ ๋ฌธ์ œ๋Š” ์œ„์— ์ œ๊ณต๋œ ์‹œ๋‚˜๋ฆฌ์˜ค๋กœ ์ธํ•ด ์ฐจํŠธ๊ฐ€ ์„ค์น˜ ์‹œ ClusterIP๋ฅผ ๋นˆ ๋ฌธ์ž์—ด๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. --force ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ์–ธ๊ธ‰ํ•œ ๊ฒƒ๊ณผ ๊ฐ™์ด ์ด ํŠน์ • ์‚ฌ๋ก€๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค๋ฅธ ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ง„๋‹จ ๋ฐ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ์•ž์„œ ์ œ๊ณต๋œ ์กฐ์–ธ๊ณผ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๋Š” ๋ณ„๋„๋กœ ๋…ผ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

@mssachan ์€ ์‚ฌ์šฉ ์‚ฌ๋ก€์— ๋Œ€ํ•ด #7082 ๋ฐ #7431์˜ ์ œ์•ˆ ์ดˆ์•ˆ์„ ์ฐธ์กฐํ•˜์„ธ์š”. ์ด ์ œ์•ˆ์€ kubectl replace โ€”force ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•˜๋ฉฐ ์ด๋Š” Helm 2์˜ helm install โ€”force ๋™์ž‘๊ณผ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

@mssachan ์€ ์‚ฌ์šฉ ์‚ฌ๋ก€์— ๋Œ€ํ•ด #7082 ๋ฐ #7431์˜ ์ œ์•ˆ ์ดˆ์•ˆ์„ ์ฐธ์กฐํ•˜์„ธ์š”. ์ด ์ œ์•ˆ์€ kubectl replace โ€”force ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•˜๋ฉฐ ์ด๋Š” Helm 2์˜ helm install โ€”force ๋™์ž‘๊ณผ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฐ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. --force ํ”Œ๋ž˜๊ทธ๋ฅผ ์ƒ๋žตํ•ด๋„ ์ฐจํŠธ๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ๋•Œ ์—ฌ์ „ํžˆ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด cert-manager :

2020-03-05 12:15:19 CRITICAL: Command returned [ 1 ] exit code and error message [ Error: UPGRADE FAILED: cannot patch "cert-manager-cainjector" with kind Deployment: Deployment.apps "cert-manager-cainjector" is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{"app":"cainjector", "app.kubernetes.io/instance":"cert-manager", "app.kubernetes.io/managed-by":"Helm", "app.kubernetes.io/name":"cainjector"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable && cannot patch "cert-manager" with kind Deployment: Deployment.apps "cert-manager" is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{"app":"cert-manager", "app.kubernetes.io/instance":"cert-manager", "app.kubernetes.io/managed-by":"Helm", "app.kubernetes.io/name":"cert-manager"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable && cannot patch "cert-manager-webhook" with kind Deployment: Deployment.apps "cert-manager-webhook" is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{"app":"webhook", "app.kubernetes.io/instance":"cert-manager", "app.kubernetes.io/managed-by":"Helm", "app.kubernetes.io/name":"webhook"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable

@mssachan ์€ ์‚ฌ์šฉ ์‚ฌ๋ก€์— ๋Œ€ํ•ด #7082 ๋ฐ #7431์˜ ์ œ์•ˆ ์ดˆ์•ˆ์„ ์ฐธ์กฐํ•˜์„ธ์š”. ์ด ์ œ์•ˆ์€ kubectl replace โ€”force ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•˜๋ฉฐ ์ด๋Š” Helm 2์˜ helm install โ€”force ๋™์ž‘๊ณผ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฐ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. --force ํ”Œ๋ž˜๊ทธ๋ฅผ ์ƒ๋žตํ•ด๋„ ์ฐจํŠธ๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ๋•Œ ์—ฌ์ „ํžˆ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด cert-manager :

2020-03-05 12:15:19 CRITICAL: Command returned [ 1 ] exit code and error message [ Error: UPGRADE FAILED: cannot patch "cert-manager-cainjector" with kind Deployment: Deployment.apps "cert-manager-cainjector" is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{"app":"cainjector", "app.kubernetes.io/instance":"cert-manager", "app.kubernetes.io/managed-by":"Helm", "app.kubernetes.io/name":"cainjector"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable && cannot patch "cert-manager" with kind Deployment: Deployment.apps "cert-manager" is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{"app":"cert-manager", "app.kubernetes.io/instance":"cert-manager", "app.kubernetes.io/managed-by":"Helm", "app.kubernetes.io/name":"cert-manager"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable && cannot patch "cert-manager-webhook" with kind Deployment: Deployment.apps "cert-manager-webhook" is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{"app":"webhook", "app.kubernetes.io/instance":"cert-manager", "app.kubernetes.io/managed-by":"Helm", "app.kubernetes.io/name":"webhook"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable

@ sc250024 helm v2๋ฅผ v3์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•œ ํ›„์—๋„ ๋˜‘๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์—…๊ทธ๋ ˆ์ด๋“œ ์ง„ํ–‰์ด ์›ํ™œํ•˜๊ณ  ์˜ค๋ฅ˜๊ฐ€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ helm์—์„œ cert-manager๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋ ค๊ณ  ์‹œ๋„ํ–ˆ์ง€๋งŒ ๋™์ผํ•œ ์ถœ๋ ฅ์œผ๋กœ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.

# helm upgrade cert-manager jetstack/cert-manager --namespace cert-manager --atomic --cleanup-on-fail

# helm version
version.BuildInfo{Version:"v3.1.1", GitCommit:"afe70585407b420d0097d07b21c47dc511525ac8", GitTreeState:"clean", GoVersion:"go1.13.8"}

--force๊ฐ€ ์‚ฌ์šฉ๋˜์ง€ ์•Š๊ฑฐ๋‚˜ clusterIP ์— ๋Œ€ํ•œ ์˜ต์…˜์ด ์„ค์ •๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์— ๋Œ€ํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•. ์ด๊ฒƒ์€ ๋‚ด ์„œ๋น„์Šค ๋งค๋‹ˆํŽ˜์ŠคํŠธ์ž…๋‹ˆ๋‹ค.

apiVersion: v1
kind: Service
metadata:
  name: "{{ .Values.deploymentBaseName }}-{{ .Values.skaffoldUser }}"
  labels:
    name: "{{ .Values.deploymentBaseName }}-{{ .Values.skaffoldUser }}"
spec:
  ports:
    - port: {{ .Values.servicePort }}
      targetPort: {{ .Values.containerPort }}
      protocol: TCP
      name: http
    - name: debugger-http
      port: {{ .Values.debuggerPort }}
      targetPort: {{ .Values.debuggerPort }}
      protocol: TCP
  selector:
    app: "{{ .Values.deploymentBaseName }}-{{ .Values.skaffoldUser }}"
  type: ClusterIP

@davidfernandezm ์ด์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์„ ์ฐพ์€ ์ ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๋‚˜๋Š” ๋‚ด ๋์—์„œ ๊ฐ™์€ ๊ฒƒ์„๋ณด๊ณ  ์žˆ์œผ๋ฉฐ ๋‚ด ์„œ๋น„์Šค๋Š” ๊ท€ํ•˜์˜ ์„œ๋น„์Šค์™€ ๋™์ผํ•˜๊ฒŒ ์ •์˜๋ฉ๋‹ˆ๋‹ค. clusterIP ๋Œ€ํ•œ ์˜ต์…˜์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์ง€๋งŒ Helm์€ ์—ฌ์ „ํžˆ โ€‹โ€‹์—…๊ทธ๋ ˆ์ด๋“œ์— ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€

์ด๊ฒƒ๋„ ์–ป๊ณ , ๋‹ค์‹œ: ์œ„์˜ ๋‘ ๊ฐ€์ง€ ์˜๊ฒฌ.

๋” ๋งŽ์€ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•ด ์ฃผ์„ธ์š”. ์›์ธ์ด๋‚˜ ๊ท€ํ•˜์˜ ์‚ฌ๋ก€์—์„œ ์ด ๋ฌธ์ œ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ฐœ์ƒํ•˜๋Š”์ง€ ์ดํ•ดํ•˜์ง€ ์•Š๊ณ ๋Š” ๋„์›€์„ ๋“œ๋ฆด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ฐ์‚ฌ ํ•ด์š”.

@antonakv ์ด ๋ฌธ์ œ๋Š” 7956๊ณผ ์ค‘๋ณต๋ฉ๋‹ˆ๋‹ค.
@bacongobbler ์ถ”๊ฐ€ ์ •๋ณด

helm update --install ๋ช…๋ น๊ณผ ํ•จ๊ป˜ --force ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ helm v3.0.0-rc.2๋กœ ์„œ๋น„์Šค ์œ ํ˜• ๋˜๋Š” clusterIP๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์•„๋„ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. --force ์—†์ด๋Š” ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

์‹œ์›ํ•œ! ๋‚˜๋Š” helmfile yaml์—์„œ force: .. ํ–‰์— ์ฃผ์„์„ ๋‹ฌ์•„์•ผ ํ•œ๋‹ค๋Š” ๊ท€ํ•˜์˜ ๋‹ต๋ณ€์—์„œ ์˜๊ฐ์„ ์–ป์—ˆ์Šต๋‹ˆ๋‹ค.

helmDefaults:
  tillerless: true
  verify: false
  wait: true
  timeout: 600
  # force: true <---- THI ONE IS COMMENTED

์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค ๐ŸŽ‰

๋‚˜๋Š” ์œ„์˜ ๋ชจ๋“  ๊ฒƒ์„ ์‹œ๋„ํ–ˆ์ง€๋งŒ ์•„๋ฌด๋„ ๋‚˜๋ฅผ ์œ„ํ•ด ์ผํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋‚ด ์ฐจํŠธ์—์„œ nginx-ingress ์„ ๋น„ํ™œ์„ฑํ™”ํ•˜๊ณ  ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  ๋‹ค์‹œ ํ™œ์„ฑํ™”ํ•˜๊ณ  ๋‹ค์‹œ ์—…๊ทธ๋ ˆ์ด๋“œํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ํด๋ผ์šฐ๋“œ ๊ณต๊ธ‰์ž๊ฐ€ ํ• ๋‹นํ•œ IP ์ฃผ์†Œ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์ง€๋งŒ ์•„๋ฌด๋Ÿฐ ํ”ผํ•ด๊ฐ€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

helm update --install ๋ช…๋ น๊ณผ ํ•จ๊ป˜ --force ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ helm v3.0.0-rc.2๋กœ ์„œ๋น„์Šค ์œ ํ˜• ๋˜๋Š” clusterIP๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์•„๋„ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. --force ์—†์ด๋Š” ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

์ตœ๊ณ ์˜ ์†”๋ฃจ์…˜, ๊ทธ๊ฒƒ์€ ๋‚˜๋ฅผ ์œ„ํ•ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค, ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

์šฐ๋ฆฌ๋Š” ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ๊ฒช๊ณ  ์žˆ์œผ๋ฉฐ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
์žฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

helm install in stable/inbucket 
helm upgrade in stable/inbucket 
Error: UPGRADE FAILED: cannot patch "in-inbucket" with kind Service: Service "in-inbucket" is invalid: spec.clusterIP: Invalid value: "": field is immutable

--force ๊ฐ€ ์—ฌ๊ธฐ์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ์ด์œ ๊ฐ€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๊ต์ฒด ์ „๋žต์ด๋ผ๋ฉด ์„œ๋น„์Šค๋ฅผ ์ œ๊ฑฐํ•œ ๋‹ค์Œ ๊ต์ฒดํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ force resource updates through a replacement strategy ๋กœ ๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๊นŒ?

@bacongobbler https://github.com/helm/helm/issues/7956 ์„ ํ™•์ธํ•œ ํ›„ ์ด ์Šค๋ ˆ๋“œ์— ์ ‘์†ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด์ „์˜ ๋ชจ๋“  ๋Œ“๊ธ€ ์ž‘์„ฑ์ž์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํ…œํ”Œ๋ฆฟ์— "clusterIP"๊ฐ€ ์ „ํ˜€ ์—†์ง€๋งŒ --force ํ”Œ๋ž˜๊ทธ๊ฐ€ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ ์ตœ์‹  Helm์— ์˜ค๋ฅ˜๊ฐ€ ์—ฌ์ „ํžˆ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

ํˆฌ๊ตฌ ๋ฒ„์ „: 3.4.1

"helm -n kube-system get manifest CHART_NAME | grep clusterIP"๋Š” ๊ฒฐ๊ณผ๋ฅผ ํ‘œ์‹œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์˜ค๋ฅ˜:
field is immutable && failed to replace object: Service "SERVICE_NAME" is invalid: spec.clusterIP: Invalid value: "": field is immutable

https://github.com/helm/helm/issues/6378#issuecomment -557746499์— ์ œ๊ณต๋œ ๋™์ผํ•œ ์„ค๋ช…์ด @nick4fake์˜ ๊ฒฝ์šฐ์—๋„ ์—ฌ๊ธฐ์— ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ฐจ์ด์ ์€ --force ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Kubernetes์— ์™„์ „ํžˆ ๋ Œ๋”๋ง๋œ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ํ˜„์žฌ ๋ผ์ด๋ธŒ ๊ฐœ์ฒด๋ฅผ ๊ฐ•์ œ๋กœ ๋ฎ์–ด์“ฐ๋„๋ก ์š”์ฒญํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋งค๋‹ˆํŽ˜์ŠคํŠธ์— clusterIP ํ•„๋“œ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— Kubernetes๋Š” ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ผ์ด๋ธŒ ๊ฐœ์ฒด์—์„œ clusterIP ํ•„๋“œ๋ฅผ ์ œ๊ฑฐํ•˜๋ ค๊ณ  ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฏ€๋กœ Invalid value: "": field is immutable ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

@bacongobbler ์—ฌ๊ธฐ์„œ ๋ญ”๊ฐ€ ๋†“์น˜๋ฉด ์ •๋ง ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ Helm ๋‚ด๋ถ€์— ๋Œ€ํ•ด ์ถฉ๋ถ„ํžˆ ์•Œ์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ์ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

"๋‚ด ๊ถŒ์žฅ ์‚ฌํ•ญ์€ ํ…œํ”Œ๋ฆฟ ์ถœ๋ ฅ์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. clusterIP๊ฐ€ ๊ฐ’์œผ๋กœ ์ œ๊ณต๋˜์ง€ ์•Š์œผ๋ฉด ๊ฐ’์„ ๋นˆ ๋ฌธ์ž์—ด๋กœ ์„ค์ •ํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค... ํ•„๋“œ๋ฅผ ์™„์ „ํžˆ ์ƒ๋žตํ•˜์‹ญ์‹œ์˜ค."

๊ทธ๋ž˜์„œ ํ•ด๊ฒฐ์ฑ…์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? clusterIP ํ•„๋“œ๊ฐ€ ์ผ๋ถ€ ์ •์  ๊ฐ’์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด "--force" ํ”Œ๋ž˜๊ทธ๋ฅผ ์ „ํ˜€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๊นŒ?

Kubernetes์— ๊ด€ํ•œ ํ•œ: ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค.

๋‚ด ์ดํ•ด์— ๋”ฐ๋ฅด๋ฉด "๊ฐ•์ œ๋กœ ๋ฎ์–ด์“ฐ๊ธฐ"๋Š” "์‚ญ์ œ ๋ฐ ๋‹ค์‹œ ๋งŒ๋“ค๊ธฐ"์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— Kubernetes์˜ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ์—…์ŠคํŠธ๋ฆผ ๋ฒ„๊ทธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

๋ฐ˜๋ฉด --force ๋Š” "๋Œ€์ฒด ์ „๋žต์„ ํ†ตํ•œ ๊ฐ•์ œ ๋ฆฌ์†Œ์Šค ์—…๋ฐ์ดํŠธ"๋กœ ์„ค๋ช…๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Helm๋„ ์˜คํ•ด์˜ ์†Œ์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ๋Š” ๊ต์ฒด๋ฅผ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์ง€๋งŒ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ•์ œ๋กœ ๋ฎ์–ด์“ฐ๋ ค๊ณ  ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค(ํ”Œ๋ž˜๊ทธ ์ด๋ฆ„์„ --force-overwrite ๋กœ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค). ๊ฐ•์ œ ๊ต์ฒด๋Š” ์‚ญ์ œํ•˜๊ณ  ๋‹ค์‹œ ๋งŒ๋“œ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค( --force-recreate ํ”Œ๋ž˜๊ทธ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Œ). ๋ฌผ๋ก  --force-recreate ๋Š” ์ผ๋ถ€ ๋ฆฌ์†Œ์Šค์— ์‚ฌ์šฉํ•˜๊ธฐ์—๋Š” ์•ฝ๊ฐ„ ์œ„ํ—˜ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ํ•ญ์ƒ ์„ฑ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์–ด์จŒ๋“  Helm์€ ์ด๋Ÿฌํ•œ ์œ ํ˜•์˜ ๋ฌธ์ œ์— ๋Œ€ํ•œ ๋Œ€์ฒด ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ๋™์ž‘( --force-overwrite )์ด ์‹คํŒจํ•˜๊ณ  ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š” ํ•„๋“œ ์˜ค๋ฅ˜๋ฅผ ๊ฐ์ง€ํ•˜๋ฉด ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ญ์ œํ•˜๊ณ  ๋‹ค์‹œ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค( --force-recreate ).

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰