Skip to content
Search

Helm

lastmod 2025-12-21created 2022-10-0118 edits

Kubernetes package management (like npm or pip)

Categories

Commands

  • completion - Generate autocompletion scripts for the specified shell
  • create - Create a new chart with the given name
  • dependency - Manage a chart's dependencies
  • env - Helm client environment information
  • get - Download extended information of a named release
  • help - Help about any command
  • history - Fetch release history
  • install - Install a chart
  • lint - Examine a chart for possible issues
  • list - List releases
  • package - Package a chart directory into a chart archive
  • plugin - Install, list, or uninstall Helm plugins
  • pull - Download a chart from a repository and (optionally) extract it in a local directory
  • push - Push a chart to a remote
  • registry - Log in to or log out from a registry
  • repo - Add, list, remove, update, and index chart repositories
  • rollback - Roll back a release to a previous revision
  • search - Search for a keyword in charts
  • show - Show information about a chart
  • status - Display the status of the named release
  • template - Locally render templates
  • test - Run tests for a release
  • uninstall - Uninstall a release
  • upgrade - Upgrade a release
  • verify - Verify that a chart at the given path has been signed and is valid
  • version - Print Helm version information

Purpose of Helm

Helm is a tool for managing Kubernetes packages called "charts". With Helm you can do the following.

  • Create new charts from scratch
  • Package charts into chart archive (tgz) files
  • Interact with chart repositories, where charts are stored
  • Install and uninstall charts in a Kubernetes cluster
  • Manage the release cycle of charts installed with Helm

Install

If you have snap, install it with that.

sudo snap install helm --classic

Concepts

Helm has three important concepts.

Chart
A bundle of information needed to create an instance of a Kubernetes application.
Config
Contains configuration information that can be merged into a packaged chart to create a releasable object.
Release
A running instance of a "chart", combined with a specific "config".

Deployment

Installing Nginx

# Stable Chart
helm repo add stable https://charts.helm.sh/stable

# Bitnami NginX는 통합된 php, mysql, nginx 개발환경을 제공한다.
helm repo add bitnami https://charts.bitnami.com/bitnami

# Helm Chart를 업데이트 해준다.
helm repo update

# 이번 실습에선 nginx 서버를 설치해야 하므로 nginx와 관련된 repository를 검색한다.
helm search repo nginx

# bitnami/nginx repository를 통해 서버를 설치한다.
helm install nginx bitnami/nginx

The output looks like this:

NAME: nginx
LAST DEPLOYED: Thu Dec 18 16:00:38 2025
NAMESPACE: default
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None
NOTES:
CHART NAME: nginx
CHART VERSION: 22.3.9
APP VERSION: 1.29.4

⚠ WARNING: Since August 28th, 2025, only a limited subset of images/charts are available for free.
    Subscribe to Bitnami Secure Images to receive continued support and security updates.
    More info at https://bitnami.com and https://github.com/bitnami/containers/issues/83267

** Please be patient while the chart is being deployed **
NGINX can be accessed through the following DNS name from within your cluster:

    nginx.default.svc.cluster.local (port 80)

To access NGINX from outside the cluster, follow the steps below:

1. Get the NGINX URL by running these commands:

  NOTE: It may take a few minutes for the LoadBalancer IP to be available.
        Watch the status with: 'kubectl get svc --namespace default -w nginx'

    export SERVICE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].port}" services nginx)
    export SERVICE_IP=$(kubectl get svc --namespace default nginx -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    echo "http://${SERVICE_IP}:${SERVICE_PORT}"
WARNING: Rolling tag detected (bitnami/nginx:latest), please note that it is strongly recommended to avoid using rolling tags in a production environment.
+info https://techdocs.broadcom.com/us/en/vmware-tanzu/application-catalog/tanzu-application-catalog/services/tac-doc/apps-tutorials-understand-rolling-tags-containers-index.html
WARNING: Rolling tag detected (bitnami/git:latest), please note that it is strongly recommended to avoid using rolling tags in a production environment.
+info https://techdocs.broadcom.com/us/en/vmware-tanzu/application-catalog/tanzu-application-catalog/services/tac-doc/apps-tutorials-understand-rolling-tags-containers-index.html
WARNING: Rolling tag detected (bitnami/nginx-exporter:latest), please note that it is strongly recommended to avoid using rolling tags in a production environment.
+info https://techdocs.broadcom.com/us/en/vmware-tanzu/application-catalog/tanzu-application-catalog/services/tac-doc/apps-tutorials-understand-rolling-tags-containers-index.html

WARNING: There are "resources" sections in the chart not set. Using "resourcesPreset" is not recommended for production. For production installations, please set the following values according to your workload needs:
  - cloneStaticSiteFromGit.gitSync.resources
  - resources
+info https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/

In short, find the IP address with one of these two methods and connect:

# 방법 1
kubectl get svc --namespace default -w nginx

# 방법 2
export SERVICE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].port}" services nginx)
export SERVICE_IP=$(kubectl get svc --namespace default nginx -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo "http://${SERVICE_IP}:${SERVICE_PORT}"

Fixing the connection problem

However, if you have installed something like k3s or a separate load balancer, you will probably get output like this:

kubectl get svc --namespace default -w nginx

Output:

NAME    TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
nginx   LoadBalancer   10.43.132.171   <pending>     80:30554/TCP,443:32117/TCP   35s

EXTERNAL-IP stays in the <pending> state. To find the problem, look up the related pod:

kubectl get pods -n kube-system

The output looks like this:

NAME                                      READY   STATUS      RESTARTS   AGE
coredns-6d668d687-sk8z4                   1/1     Running     0          28m
helm-install-traefik-btnrc                0/1     Completed   1          28m
helm-install-traefik-crd-thplf            0/1     Completed   0          28m
local-path-provisioner-869c44bfbd-khd2x   1/1     Running     0          28m
metrics-server-7bfffcd44-bp7lr            1/1     Running     0          28m
svclb-nginx-526a66f9-pbzkk                0/2     Pending     0          8m20s
svclb-traefik-33133399-bhx7l              2/2     Running     0          27m
traefik-865bd56545-995lq                  1/1     Running     0          27m

You can see that the nginx-related pod is READY 0/2 and in the Pending state.

Inspect that pod (svclb-nginx-526a66f9-pbzkk):

kubectl describe pod svclb-nginx-526a66f9-pbzkk -n kube-system

The output looks like this:

Name:                 svclb-nginx-526a66f9-pbzkk
Namespace:            kube-system
Priority:             2000001000
Priority Class Name:  system-node-critical
Service Account:      svclb
Node:                 <none>
Labels:               app=svclb-nginx-526a66f9
                      controller-revision-hash=7b448cb6fb
                      pod-template-generation=1
                      svccontroller.k3s.cattle.io/svcname=nginx
                      svccontroller.k3s.cattle.io/svcnamespace=default
Annotations:          <none>
Status:               Pending
IP:
IPs:                  <none>
Controlled By:        DaemonSet/svclb-nginx-526a66f9
Containers:
  lb-tcp-80:
    Image:      rancher/klipper-lb:v0.4.13
    Port:       80/TCP (lb-tcp-80)
    Host Port:  80/TCP (lb-tcp-80)
    Environment:
      SRC_PORT:    80
      SRC_RANGES:  0.0.0.0/0
      DEST_PROTO:  TCP
      DEST_PORT:   80
      DEST_IPS:    10.43.132.171
    Mounts:        <none>
  lb-tcp-443:
    Image:      rancher/klipper-lb:v0.4.13
    Port:       443/TCP (lb-tcp-443)
    Host Port:  443/TCP (lb-tcp-443)
    Environment:
      SRC_PORT:    443
      SRC_RANGES:  0.0.0.0/0
      DEST_PROTO:  TCP
      DEST_PORT:   443
      DEST_IPS:    10.43.132.171
    Mounts:        <none>
Conditions:
  Type           Status
  PodScheduled   False
Volumes:         <none>
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     CriticalAddonsOnly op=Exists
                 node-role.kubernetes.io/control-plane:NoSchedule op=Exists
                 node-role.kubernetes.io/master:NoSchedule op=Exists
                 node.kubernetes.io/disk-pressure:NoSchedule op=Exists
                 node.kubernetes.io/memory-pressure:NoSchedule op=Exists
                 node.kubernetes.io/not-ready:NoExecute op=Exists
                 node.kubernetes.io/pid-pressure:NoSchedule op=Exists
                 node.kubernetes.io/unreachable:NoExecute op=Exists
                 node.kubernetes.io/unschedulable:NoSchedule op=Exists
Events:
  Type     Reason            Age    From               Message
  ----     ------            ----   ----               -------
  Warning  FailedScheduling  9m10s  default-scheduler  0/1 nodes are available: 1 node(s) didn't have free ports for the requested pod ports. preemption: 0/1 nodes are available: 1 node(s) didn't have free ports for the requested pod ports.
  Warning  FailedScheduling  3m47s  default-scheduler  0/1 nodes are available: 1 node(s) didn't have free ports for the requested pod ports. preemption: 0/1 nodes are available: 1 node(s) didn't have free ports for the requested pod ports.

A warning event is printed at the bottom:

0/1 nodes are available: 1 node(s) didn't have free ports for the requested pod ports. preemption: 0/1 nodes are available: 1 node(s) didn't have free ports for the requested pod ports.

Translated into Korean:

사용 가능한 노드는 0/1개입니다. 요청된 Pod 포트에 사용할 수 있는 여유 포트가 1개 노드에 없습니다. 선점: 사용 가능한 노드는 0/1개입니다. 요청된 Pod 포트에 사용할 수 있는 여유 포트가 1개 노드에 없습니다.

In K3s, the Traefik Ingress Controller occupies ports 80/443 by default.

How to check port usage:

# 80, 443 포트를 사용하는 프로세스 확인
sudo netstat -tulpn | grep ':80\|:443'

# 또는
sudo ss -tulpn | grep ':80\|:443'

# Traefik 서비스 확인
kubectl get svc -n kube-system traefik

Accessing through Traefik Ingress instead of a LoadBalancer is the standard approach in K3s:

# Helm으로 Ingress 포함하여 재설치
helm upgrade nginx bitnami/nginx \
  --set service.type=ClusterIP \
  --set ingress.enabled=true \
  --set ingress.hostname=nginx.local

In this case you must connect via nginx.local, the hostname you specified. Update the /etc/hosts file or something.

Note that you can also use different ports: (not recommended)

helm upgrade nginx bitnami/nginx \
  --set service.ports.http=8080 \
  --set service.ports.https=8443

Troubleshooting

WARNING: Kubernetes configuration file is ***-readable. This is insecure. Location: ~/.kube/config

Running related commands prints warnings like these.

WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: ~/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: ~/.kube/config

Change the access permissions with the chmod 600 ~/.kube/config command.

See also

Favorite site

author Zer0 sitemap