{"id":10631,"date":"2025-05-06T16:36:43","date_gmt":"2025-05-06T07:36:43","guid":{"rendered":"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=10631"},"modified":"2025-08-07T12:28:02","modified_gmt":"2025-08-07T03:28:02","slug":"kubernetes-private-docker-repository-with-pv-%ec%84%a4%ec%b9%98-2025","status":"publish","type":"post","link":"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=10631","title":{"rendered":"Kubernetes \u2013 Private Docker Registry with PV \uc124\uce58 2025"},"content":{"rendered":"<h1>Kubernetes \u2013 Private Docker Registry with PV \uc124\uce58<\/h1>\n<p>Private Docker Registry \ub97c \uc124\uce58\ud558\ub294 \ubc29\ubc95\uc744 \uc124\uba85\ud569\ub2c8\ub2e4.<\/p>\n<p>\uc77c\ubc18 \ub3c4\uba54\uc778 \uc8fc\uc18c\uc640 https \uc778\uc99d\uc11c\ub97c \uc774\uc6a9\ud574 \uc124\uc815\ud574\uc57c \ud569\ub2c8\ub2e4.<\/p>\n<h2>Namespace<\/h2>\n<pre><code class=\"language-bash\">vi registry-namespace.yaml\n---------------------------\napiVersion: v1\nkind: Namespace\nmetadata:\n  name: docker-registry\n---------------------------<\/code><\/pre>\n<pre><code class=\"language-bash\">kubectl apply -f registry-namespace.yaml<\/code><\/pre>\n<h2>PersistentVolume \/ PersistentVolumeClaim \uc0dd\uc131<\/h2>\n<pre><code class=\"language-bash\">sudo mkdir -p \/data\/docker-registry\nsudo chmod 777 -R \/data\/docker-registry<\/code><\/pre>\n<pre><code class=\"language-bash\">kubectl get nodes<\/code><\/pre>\n<pre><code class=\"language-bash\">vi registry-pv.yaml\n---------------------------\napiVersion: v1\nkind: PersistentVolume\nmetadata:\n  name: docker-registry-pv\nspec:\n  capacity:\n    storage: 100Gi\n  accessModes:\n    - ReadWriteOnce\n  persistentVolumeReclaimPolicy: Retain\n  storageClassName: local-storage\n  local:\n    path: \/data\/docker-registry\n  nodeAffinity:\n    required:\n      nodeSelectorTerms:\n      - matchExpressions:\n        - key: kubernetes.io\/hostname\n          operator: In\n          values:\n          - your-node-name  # \uc2e4\uc81c \ub178\ub4dc \uc774\ub984\uc73c\ub85c \ubcc0\uacbd\n---------------------------<\/code><\/pre>\n<pre><code class=\"language-bash\">kubectl apply -f registry-pv.yaml<\/code><\/pre>\n<pre><code class=\"language-bash\">vi registry-pvc.yaml\n---------------------------\napiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  name: docker-registry-pvc\n  namespace: docker-registry\nspec:\n  accessModes:\n    - ReadWriteOnce\n  resources:\n    requests:\n      storage: 100Gi\n  storageClassName: local-storage\n---------------------------<\/code><\/pre>\n<pre><code class=\"language-bash\">kubectl apply -f registry-pvc.yaml<\/code><\/pre>\n<pre><code class=\"language-bash\">kubectl get pv,pvc\nNAME                                  CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                 STORAGECLASS    VOLUMEATTRIBUTESCLASS   REASON   AGE\npersistentvolume\/docker-registry-pv   100Gi      RWO            Retain           Bound    docker-registry\/docker-registry-pvc   local-storage   &lt;unset&gt;                          2m27s<\/code><\/pre>\n<h2>TLS<\/h2>\n<p>\uc778\uc99d\uc11c \ubcc0\uacbd\uc5d0 \ub300\ube44\ud574\uc11c \ud06c\ub860\ud0ed\ub4f1\uc5d0 \ub4f1\ub85d\ud574\uc11c \uac31\uc2e0\uc744 \uc790\ub3d9\ud654\ud574\uc57c \ud569\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-bash\"># kubectl delete secret docker-registry-tls -n docker-registry\nkubectl create secret tls docker-registry-tls --cert=\/home\/skyer9\/cert\/fullchain.pem --key=\/home\/skyer9\/cert\/privkey.pem -n docker-registry<\/code><\/pre>\n<h2>Deployment<\/h2>\n<pre><code class=\"language-bash\">kubectl create secret generic docker-registry-secret \\\n  --from-literal=http-secret=$(openssl rand -hex 32) \\\n  -n docker-registry<\/code><\/pre>\n<pre><code class=\"language-bash\">vi registry-deployment.yaml\n---------------------------\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: docker-registry\n  namespace: docker-registry\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: docker-registry\n  template:\n    metadata:\n      labels:\n        app: docker-registry\n    spec:\n      containers:\n      - name: registry\n        image: registry:2\n        ports:\n        - containerPort: 5000\n        env:\n        - name: REGISTRY_HTTP_SECRET\n          valueFrom:\n            secretKeyRef:\n              name: docker-registry-secret\n              key: http-secret\n        - name: REGISTRY_HTTP_TLS_CERTIFICATE\n          value: \/certs\/tls.crt\n        - name: REGISTRY_HTTP_TLS_KEY\n          value: \/certs\/tls.key\n        volumeMounts:\n        - name: registry-data\n          mountPath: \/data\/registry\n        - name: tls-certs\n          mountPath: \/certs\n          readOnly: true\n        resources:\n          requests:\n            memory: &quot;256Mi&quot;\n            cpu: &quot;250m&quot;\n          limits:\n            memory: &quot;512Mi&quot;\n            cpu: &quot;500m&quot;\n      volumes:\n      - name: registry-data\n        persistentVolumeClaim:\n          claimName: docker-registry-pvc\n      - name: tls-certs\n        secret:\n          secretName: docker-registry-tls\n---------------------------<\/code><\/pre>\n<pre><code class=\"language-bash\">kubectl apply -f registry-deployment.yaml<\/code><\/pre>\n<h2>Service<\/h2>\n<pre><code class=\"language-bash\">vi registry-service.yaml\n---------------------------\napiVersion: v1\nkind: Service\nmetadata:\n  name: docker-registry-service\n  namespace: docker-registry\nspec:\n  selector:\n    app: docker-registry\n  ports:\n  - name: registry\n    port: 5000\n    targetPort: 5000\n  type: ClusterIP\n---------------------------<\/code><\/pre>\n<pre><code class=\"language-bash\">kubectl apply -f registry-service.yaml<\/code><\/pre>\n<h2>Ingress<\/h2>\n<p>Ingress \ub294 80\/443 \ud3ec\ud2b8\ub85c\uc758 \uc811\uc18d\ub9cc \ud5c8\uc6a9\ud569\ub2c8\ub2e4.<br \/>\n\uc989 \uc11c\ube44\uc2a4\ub97c \uc0c8\ub85c \ub530\ub824\uba74 \ub3c4\uba54\uc778\ub3c4 \uc0c8\ub85c \ub530\uc57c \ud569\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-bash\">vi registry-ingress.yaml\n---------------------------\napiVersion: networking.k8s.io\/v1\nkind: Ingress\nmetadata:\n  name: docker-registry-ingress\n  namespace: docker-registry\n  annotations:\n    nginx.ingress.kubernetes.io\/ssl-redirect: &quot;true&quot;\n    nginx.ingress.kubernetes.io\/proxy-body-size: &quot;0&quot;\n    nginx.ingress.kubernetes.io\/proxy-read-timeout: &quot;600&quot;\n    nginx.ingress.kubernetes.io\/proxy-send-timeout: &quot;600&quot;\n    # GitHub Actions IP \ub300\uc5ed\ub9cc \ud5c8\uc6a9 (\uc608\uc2dc)\n    nginx.ingress.kubernetes.io\/whitelist-source-range: &quot;192.30.252.0\/22,185.199.108.0\/22,140.82.112.0\/20,143.55.64.0\/20,20.201.28.151\/32,20.205.243.166\/32,20.87.225.212\/32,20.248.137.48\/32,20.207.73.82\/32,20.27.177.113\/32,20.29.134.23\/32,20.87.245.0\/32,20.201.28.152\/32,20.205.243.160\/32,102.133.202.242\/32&quot;\nspec:\n  ingressClassName: nginx\n  tls:\n  - hosts:\n    - k8s.example.co.kr                    # \ub3c4\uba54\uc778\uc744 \ubcc0\uacbd\ud558\uc138\uc694.\n    secretName: docker-registry-tls\n  rules:\n  - host: k8s.example.co.kr                # \ub3c4\uba54\uc778\uc744 \ubcc0\uacbd\ud558\uc138\uc694.\n    http:\n      paths:\n      - path: \/\n        pathType: Prefix\n        backend:\n          service:\n            name: docker-registry-service\n            port:\n              number: 5000\n---------------------------<\/code><\/pre>\n<pre><code class=\"language-bash\">kubectl apply -f registry-ingress.yaml<\/code><\/pre>\n<p><a href=\"https:\/\/k8s.example.co.kr\/\">https:\/\/k8s.example.co.kr\/<\/a> \ub85c \uc811\uc18d\ud569\ub2c8\ub2e4.<\/p>\n<h2>80\/443 \ud3ec\ud2b8\ub97c \uc0ac\uc6a9\ud560 \uc218 \uc5c6\ub294 \uacbd\uc6b0<\/h2>\n<pre><code class=\"language-bash\">vi registry-service.yaml\n---------------------------\napiVersion: v1\nkind: Service\nmetadata:\n  name: docker-registry-service\n  namespace: docker-registry\nspec:\n  selector:\n    app: docker-registry\n  ports:\n  - name: registry\n    port: 5000\n    targetPort: 5000\n    nodePort: 30010\n    protocol: TCP\n  type: NodePort\n---------------------------<\/code><\/pre>\n<pre><code class=\"language-bash\">kubectl apply -f registry-service.yaml<\/code><\/pre>\n<p><a href=\"https:\/\/\ub178\ub4dc\uc544\uc774\ud53c:30010\/\">https:\/\/\ub178\ub4dc\uc544\uc774\ud53c:30010\/<\/a> \ub85c \uc811\uc18d\ud569\ub2c8\ub2e4.<\/p>\n<p>&quot;\uc8fc\uc758 \uc694\ud568&quot; \uc774\ub77c\ub294 \ubb38\uad6c\uac00 \ub0a8\uc544\uc788\uc744 \uc218 \uc788\ub2e4.<br \/>\n\uc774\ub7f4 \ub54c\ub294 \ub2e4\ub978 \ube0c\ub77c\uc6b0\uc800(\uc5e3\uc9c0, \uc0ac\ud30c\ub9ac \ub4f1\ub4f1) \ub97c \uc774\uc6a9\ud574 \uc811\uc18d\ud558\uba74 &quot;\uc8fc\uc758 \uc694\ud568&quot; \ubb38\uad6c\uac00 \ud45c\uc2dc\ub418\uc9c0 \uc54a\ub294 \uac83\uc744 \ud655\uc778\ud560 \uc218 \uc788\ub2e4.<\/p>\n<h2>\ub85c\uadf8\uc778 \uc81c\ud55c<\/h2>\n<p>\uae30\ubcf8 \uc124\uc815\uc740 \ub204\uad6c\ub098 \uacc4\uc815\uc744 \uc0dd\uc131\uacfc \ub3d9\uc2dc\uc5d0 \ub85c\uadf8\uc778\ud560 \uc218 \uc788\ub3c4\ub85d \ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4.<br \/>\n\uacc4\uc815 \uc0dd\uc131\uc744 \uc81c\ud55c \ud558\ub824\uba74 htpasswd \ub4f1\uc744 \uc774\uc6a9\ud574 \uacc4\uc815\uc815\ubcf4\ub97c \uc81c\ud55c\ud574\uc57c \ud569\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-bash\">docker login k8s.example.co.kr:30010\nUsername: test\nPassword:\n\nWARNING! Your credentials are stored unencrypted in &#039;\/home\/skyer9\/.docker\/config.json&#039;.\nConfigure a credential helper to remove this warning. See\nhttps:\/\/docs.docker.com\/go\/credential-store\/\n\nLogin Succeeded<\/code><\/pre>\n<pre><code class=\"language-bash\"># sudo apt install apache2-utils\nhtpasswd -Bbn username password &gt; htpasswd\n# htpasswd -Bb htpasswd another_user another_password\n\nkubectl create secret generic docker-registry-htpasswd \\\n  --from-file=htpasswd=.\/htpasswd \\\n  -n docker-registry<\/code><\/pre>\n<pre><code class=\"language-bash\">vi registry-deployment.yaml\n---------------------------\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: docker-registry\n  namespace: docker-registry\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: docker-registry\n  template:\n    metadata:\n      labels:\n        app: docker-registry\n    spec:\n      containers:\n      - name: registry\n        image: registry:2\n        ports:\n        - containerPort: 5000\n        env:\n        - name: REGISTRY_HTTP_SECRET\n          valueFrom:\n            secretKeyRef:\n              name: docker-registry-secret\n              key: http-secret\n        - name: REGISTRY_HTTP_TLS_CERTIFICATE\n          value: \/certs\/tls.crt\n        - name: REGISTRY_HTTP_TLS_KEY\n          value: \/certs\/tls.key\n        # \uc778\uc99d \uc124\uc815 \ucd94\uac00\n        - name: REGISTRY_AUTH\n          value: htpasswd\n        - name: REGISTRY_AUTH_HTPASSWD_REALM\n          value: Registry Realm\n        - name: REGISTRY_AUTH_HTPASSWD_PATH\n          value: \/auth\/htpasswd\n        volumeMounts:\n        - name: registry-data\n          mountPath: \/data\/registry\n        - name: tls-certs\n          mountPath: \/certs\n          readOnly: true\n        # htpasswd \ubcfc\ub968 \ub9c8\uc6b4\ud2b8 \ucd94\uac00\n        - name: auth\n          mountPath: \/auth\n          readOnly: true\n        resources:\n          requests:\n            memory: &quot;256Mi&quot;\n            cpu: &quot;250m&quot;\n          limits:\n            memory: &quot;512Mi&quot;\n            cpu: &quot;500m&quot;\n      volumes:\n      - name: registry-data\n        persistentVolumeClaim:\n          claimName: docker-registry-pvc\n      - name: tls-certs\n        secret:\n          secretName: docker-registry-tls\n      # htpasswd \ubcfc\ub968 \ucd94\uac00\n      - name: auth\n        secret:\n          secretName: docker-registry-htpasswd\n---------------------------<\/code><\/pre>\n<pre><code class=\"language-bash\">kubectl apply -f registry-deployment.yaml<\/code><\/pre>\n<p>Github Action \ub4f1\uc5d0\uc11c\uc758 \uc811\uc18d\uc744 \ud5c8\uc6a9\ud558\ub824\uba74 \uc544\uc774\ud53c \uae30\ubc18 \uc81c\ud55c\uc774 \uc5b4\ub824\uc6b0\ubbc0\ub85c, \ube44\ubc00\ubc88\ud638\ub97c \ubcf5\uc7a1\ud558\uace0 \uae38\uac8c \ub9cc\ub4e4\uc5b4\uc57c \ud55c\ub2e4&#8230;&#8230;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Kubernetes \u2013 Private Docker Registry with PV \uc124\uce58 Private Docker Registry \ub97c \uc124\uce58\ud558\ub294 \ubc29\ubc95\uc744 \uc124\uba85\ud569\ub2c8\ub2e4. \uc77c\ubc18 \ub3c4\uba54\uc778 \uc8fc\uc18c\uc640 https \uc778\uc99d\uc11c\ub97c \uc774\uc6a9\ud574 \uc124\uc815\ud574\uc57c \ud569\ub2c8\ub2e4. Namespace vi registry-namespace.yaml &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212; apiVersion: v1 kind: Namespace metadata: name: docker-registry &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212; kubectl apply -f registry-namespace.yaml PersistentVolume \/ PersistentVolumeClaim \uc0dd\uc131 sudo mkdir -p \/data\/docker-registry sudo chmod 777 -R \/data\/docker-registry kubectl get nodes\u2026 <span class=\"read-more\"><a href=\"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=10631\">Read More &raquo;<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17],"tags":[],"class_list":["post-10631","post","type-post","status-publish","format-standard","hentry","category-kubernetes"],"_links":{"self":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/10631","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10631"}],"version-history":[{"count":16,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/10631\/revisions"}],"predecessor-version":[{"id":10647,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/10631\/revisions\/10647"}],"wp:attachment":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10631"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=10631"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=10631"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}