こんにちは、ナナオです。

自宅にk8sを構築してからいろいろやりたいことが増えました。

ということで今回はk8sを使って自宅内で使えるプライベートレジストリの構築に挑戦してみました。

どうやって構築するか

うちには以前ジャンクで買ったNASがあるので、このNASをストレージにしてDockerイメージを放り込めるようにしたいです。

NASにはNFSで接続できます。

一度NAS側にDockerイメージを放り込むためのディレクトリを作成しておきます。

私の場合WebUIで設定できました

次に先ほど作成したディレクトリを参照するPVとPVCを作っていきます。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: registry-pv
spec:
  capacity:
    storage: 100Gi
  accessModes:
    - ReadWriteMany # 複数ノードから同時に読み書き可能
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nas
  nfs:
    server: 192.168.0.xx # NASのIPアドレス
    path: /docker-registry # NASの共有パス
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: registry-pvc
  namespace: kube-system
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 100Gi
  storageClassName: nas

作成したところ、エラーになりました。

│   Warning  FailedScheduling  15m                 default-scheduler  0/3 nodes are available: persistentv │

│ olumeclaim "registry-pvc" not found. not found                                                           │

│   Warning  FailedScheduling  15m                 default-scheduler  running PreFilter plugin "VolumeBind │

│ ing": error getting PVC "kube-system/registry-pvc": could not find v1.PersistentVolumeClaim "kube-system │

│ /registry-pvc"                                                                                           │

│   Normal   Scheduled         15m                 default-scheduler  Successfully assigned kube-system/do │

│ cker-registry-85dbb54f6c-zmrkl to nanaonuc6caysserver                                                    │

│   Warning  FailedMount       58s (x15 over 15m)  kubelet            MountVolume.SetUp failed for volume  │

│ "registry-pv" : mount failed: exit status 32                                                             │

│ Mounting command: mount                                                                                  │

│ Mounting arguments: -t nfs 192.168.0.94:/docker-registry /var/lib/kubelet/pods/1f2e1d4d-447c-4bcf-9987-c │

│ 930a2576654/volumes/kubernetes.io~nfs/registry-pv                                                        │

│ Output: mount: /var/lib/kubelet/pods/1f2e1d4d-447c-4bcf-9987-c930a2576654/volumes/kubernetes.io~nfs/regi │

│ stry-pv: bad option; for several filesystems (e.g. nfs, cifs) you might need a /sbin/mount.<type> helper │

│  program.                                                                                                │

│        dmesg(1) may have more information after failed mount system call.

エラーのbad option; for several filesystems (e.g. nfs, cifs) you might need a /sbin/mount.<type> helperから、NFSを扱うためのツールセット各ノードにがインストールされていないことが原因だとわかりました。

各ノード上でsudo apt install -y nfs-commonを実行すればいいんですが、ノードが増えた時のことを考えるとsshでログインして各ノードでその作業を実施するのが面倒です。

ということでAnsibleを使ってこの問題を解決します。

hosts.yamlには以下のように各ノードの情報を登録しておきます。

---
all:
  vars:
    ansible_user: username
    ansible_password: password
  children:
    control-plane:
      hosts:
        thinkcentle1:
          ansible_host: 192.168.0.14
        nuc1:
          ansible_host: 192.168.0.15
        mouse1:
          ansible_host: 192.168.0.16

playbookは以下のように定義します。

- hosts: all
  become: yes
  tasks:
    - name: nfs-commonのインストール
      apt:
        name: nfs-common
        state: present
        update_cache: yes

これを実行します。

ansible-playbook -i hosts.yaml playbook.yaml

成功したら先ほどのPVとPVCを再度適用しましょう。

kubectl apply -f docker-registry-storage.yaml

今度は成功しました!

つづけて作成したPVCにDockerイメージを格納するregistryをDeploymentで作成します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: docker-registry
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: docker-registry
  template:
    metadata:
      labels:
        app: docker-registry
    spec:
      containers:
      - name: registry
        image: registry:3
        ports:
        - containerPort: 5000
        volumeMounts:
        - name: registry-data
          mountPath: /var/lib/registry
      # 先ほど作成したPVCを割り当てる
      volumes:
      - name: registry-data
        persistentVolumeClaim:
          claimName: registry-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: docker-registry
  namespace: kube-system
spec:
  type: NodePort
  selector:
    app: docker-registry
  ports:
    - port: 5000
      targetPort: 5000
      nodePort: 30500 # ★外部(操作PC)から 192.168.0.14:30500 でアクセス可能

これを適用します。

kubectl apply -f docker-registry-deploy.yaml

これでレジストリの構築はできました。

次にDocker desktopの設定にinsecure-registriesを追加します。

{
  "builder": {
    "gc": {
      "defaultKeepStorage": "20GB",
      "enabled": true
    }
  },
  "experimental": false,
  "insecure-registries": ["192.168.0.14:30500"]
}

以下のコマンドでプッシュしてみます。

docker tag library-checker:0.1.0 192.168.0.14:30500/library-checker:0.1.0
docker push 192.168.0.14:30500/library-checker:0.1.0

プッシュに成功しました!

実際にこのイメージを使用したDeploymentを作成しましょう。

以前この記事でデプロイしたDockerイメージの宛先を今回プッシュしたDockerイメージに変更します。

        # ...イメージの宛先を構築したプライベートレジストリのイメージURLにする
        image: 192.168.0.14:30500/library-checker:0.1.0

ただ、結果はErrImagePullになってしまいます。

kubectl apply -fした結果は以下の通りです。

# ...中略...
Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Normal   Scheduled  33s                default-scheduler  Successfully assigned default/library-checker-689fb88597-8s2gj to mouse1
  Normal   Pulled     33s                kubelet            Container image "selenium/standalone-chrome:4.39.0-20251212" already present on machine
  Normal   Created    33s                kubelet            Created container: chrome
  Normal   Started    33s                kubelet            Started container chrome
  Normal   Pulling    17s (x2 over 33s)  kubelet            Pulling image "192.168.0.14:30500/library-checker:0.1.0"
  Warning  Failed     17s (x2 over 33s)  kubelet            Failed to pull image "192.168.0.14:30500/library-checker:0.1.0": failed to pull and unpack image "192.168.0.14:30500/library-checker:0.1.0": failed to resolve reference "192.168.0.14:30500/library-checker:0.1.0": failed to do request: Head "https://192.168.0.14:30500/v2/library-checker/manifests/0.1.0": http: server gave HTTP response to HTTPS client
  Warning  Failed     17s (x2 over 33s)  kubelet            Error: ErrImagePull
  Normal   BackOff    3s (x2 over 32s)   kubelet            Back-off pulling image "192.168.0.14:30500/library-checker:0.1.0"
  Warning  Failed     3s (x2 over 32s)   kubelet            Error: ImagePullBackOff

failed to resolve reference "192.168.0.14:30500/library-checker:0.1.0": failed to do request: Head "https://192.168.0.14:30500/v2/library-checker/manifests/0.1.0": http: server gave HTTP response to HTTPS clientとなっており、イメージのプルに失敗していることがわかります。

各ノードに追加で設定が必要だということがわかりました。

以下の公式ドキュメントを参考にして、各ノードに設定を反映していきます。

https://docs.k3s.io/ja/installation/private-registry

以下のようにregistries.yamlを定義します。

mirrors:
  "mydocker.io": # レジストリへのアクセス先
    endpoint:
      - "http://192.168.0.14:30500"

このファイルをAnsibleを使って各ノードに配布します。

- hosts: all
  become: yes
  tasks:
    - name: nfs-commonのインストール
      apt:
        name: nfs-common
        state: present
        update_cache: yes

    - name: /etc/rancher/k3s ディレクトリの作成
      file:
        path: /etc/rancher/k3s
        state: directory
        mode: '0755'

    - name: registries.yaml の配置
      copy:
        src: ./registries.yaml
        dest: /etc/rancher/k3s/registries.yaml
        owner: root
        group: root
        mode: '0644'
      notify:
        - restart_k3s

  handlers:
    - name: restart_k3s
      systemd:
        name: k3s
        state: restarted
      ignore_errors: yes

実行します。

❯ ansible-playbook -i update-node-ansible/hosts.yaml update-node-ansible/playbook.yaml -K
BECOME password:
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

PLAY [all] *************************************************************************************************

TASK [Gathering Facts] *************************************************************************************
[WARNING]: Host 'thinkcentle1' is using the discovered Python interpreter at '/usr/bin/python3.12', but future installation of another Python interpreter could cause a different interpreter to be discovered. See https://docs.ansible.com/ansible-core/2.20/reference_appendices/interpreter_discovery.html for more information.
ok: [thinkcentle1]
[WARNING]: Host 'mouse1' is using the discovered Python interpreter at '/usr/bin/python3.12', but future installation of another Python interpreter could cause a different interpreter to be discovered. See https://docs.ansible.com/ansible-core/2.20/reference_appendices/interpreter_discovery.html for more information.
ok: [mouse1]
[WARNING]: Host 'nuc1' is using the discovered Python interpreter at '/usr/bin/python3.12', but future installation of another Python interpreter could cause a different interpreter to be discovered. See https://docs.ansible.com/ansible-core/2.20/reference_appendices/interpreter_discovery.html for more information.
ok: [nuc1]

TASK [nfs-commonのインストール] ****************************************************************************
ok: [thinkcentle1]
ok: [mouse1]
ok: [nuc1]

TASK [/etc/rancher/k3s ディレクトリの作成] *****************************************************************
ok: [thinkcentle1]
ok: [mouse1]
ok: [nuc1]

TASK [registries.yaml の配置] ******************************************************************************
changed: [thinkcentle1]
changed: [mouse1]
changed: [nuc1]

RUNNING HANDLER [restart_k3s] ******************************************************************************
changed: [thinkcentle1]
changed: [nuc1]
changed: [mouse1]

PLAY RECAP *************************************************************************************************
mouse1                     : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
nuc1                       : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
thinkcentle1               : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

実行に成功しました。

レジストリのアクセス先を変更します。

        image: mydocker.io/library-checker:0.1.0

これで再度kubectl apply -fを実行すると…

今度はちゃんと動いています!

感想

意外とプライベートレジストリの構築が簡単にできて拍子抜けしちゃいました。

しかしk8s、おもしろっ(デスノートリューク風)

今後ももっと活用していきたいです。