【Harbor】プライベートなDockerRegisteryをK8s上に構築して使ってみる


みなさんこんにちは。

最近、DockerRegistryをmicrok8sのRegistryアドオンから、Harborに移行したので、その話を書きたいと思います。

## 環境

```txt
$ uname -a
Linux node08.k8s.unipro.infra 6.8.0-48-generic #48-Ubuntu SMP PREEMPT_DYNAMIC Fri Sep 27 14:04:52 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
```

ノードは5つあり、すべてmicrok8sです。

## そもそもHarborとは

Harborは、VMwareが開発しているDockerレジストリ、HelmRepoを構築できるOSSです。
デフォで認証もついていたり、いろいろできるので、結構便利です。

## 前提条件

これをするには、IngressとCert-managerがあったほうがいいですね。
私は今回、外部に公開したため、ドメインと紐つけています。
ですが、内部ネットのみでも普通に動きます。

## Helmで入れていく

### values.yamlをいじる

まず、values.yamlをいじるために、ダウンロードしてきます。

```shell
$ helm repo add harbor https://helm.goharbor.io
"harbor" has been added to your repositories

$ helm repo list
NAME            URL
harbor          https://helm.goharbor.io

$ helm pull harbor/harbor --untar
cd harbor
```

これで、

```shell
$ vi values.yaml
```

でいじれると思います。

```diff
diff -u harbor/values.yaml harbor-new/values.yaml
--- harbor/values.yaml 2024-11-19 08:54:39.689945796 +0900
+++ harbor-new/values.yaml 2024-11-18 21:18:31.926678924 +0900
@@ -28,7 +28,7 @@
       secretName: ""
   ingress:
     hosts:
-      core: core.harbor.domain
+      core: registry.uniproject-tech.net
     # set to the type of ingress controller if it has specific requirements.
     # leave as `default` for most ingress controllers.
     # set to `gce` if using the GCE ingress controller
@@ -38,7 +38,7 @@
     controller: default
     ## Allow .Capabilities.KubeVersion.Version to be overridden while creating ingress
     kubeVersionOverride: ""
-    className: ""
+    className: "nginx"
     annotations:
       # note different ingress controllers may require a different ssl-redirect annotation
       # for Envoy, use ingress.kubernetes.io/force-ssl-redirect: "true" and remove the nginx lines below
@@ -46,6 +46,7 @@
       ingress.kubernetes.io/proxy-body-size: "0"
       nginx.ingress.kubernetes.io/ssl-redirect: "true"
       nginx.ingress.kubernetes.io/proxy-body-size: "0"
+      cert-manager.io/cluster-issuer: le-cf-prod
     # ingress-specific labels
     labels: {}
   clusterIP:
@@ -109,7 +110,7 @@
 # the IP address of k8s node
 #
 # If Harbor is deployed behind the proxy, set it as the URL of proxy
-externalURL: https://core.harbor.domain
+externalURL: https://registry.uniproject-tech.net
 
 # The persistence is enabled by default and a default StorageClass
 # is needed in the k8s cluster to provision volumes dynamically.

@@ -272,7 +273,7 @@
 # key in secret is given via (default to HARBOR_ADMIN_PASSWORD)
 # existingSecretAdminPassword:
 existingSecretAdminPasswordKey: HARBOR_ADMIN_PASSWORD
-harborAdminPassword: "Harbor12345"
+harborAdminPassword: "xxxxxxxxxxx"
 
 # The internal TLS used for harbor components secure communicating. In order to enable https
 # in each component tls cert files need to provided in advance.
@@ -358,7 +359,7 @@
 caSecretName: ""
 
 # The secret key used for encryption. Must be a string of 16 chars.
-secretKey: "not-a-secure-key"
+secretKey: "xxxxxxxxxxxxxxxx"
 # If using existingSecretSecretKey, the key must be secretKey
 existingSecretSecretKey: ""
```

今編集したのは、

- パスワード/シークレット系
- Ingressと外部IPの設定

ですね。
ここは各自変えてください。

### インストール

```shell
$ helm upgrade --install harbor -n harbor -f values.yaml harbor/harbor --create-namespase

NAME: harbor
LAST DEPLOYED: Sat Sep 18 21:27:38 2021
NAMESPACE: harbor
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Please wait for several minutes for Harbor deployment to complete.
Then you should be able to visit the Harbor portal at https://192.168.10.61:30003
For more details, please visit https://github.com/goharbor/harbor
```

## NFSで永続化

これちょっぴりめんどくさかったですね。
というのも、vlaues.yamlにそういった設定項目がなく、PVは自分で作りました。

### PVCを確認

まず、PVCを確認して、どのようなボリュームが必要かを調べます。

```shell
$ kubectl get pvc -n harbor

# 私の場合、すでに設定してあるので、表示が違うかと思いますが、名前とキャパのところが確認できればOKです。
NAME                              STATUS   VOLUME              CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
data-harbor-redis-0               Bound    harbor-redis        1Gi        RWO            slow           <unset>                 32h
data-harbor-trivy-0               Bound    harbor-trivy        5Gi        RWO            slow           <unset>                 32h
database-data-harbor-database-0   Bound    harbor-db           1Gi        RWO            slow           <unset>                 32h
harbor-jobservice                 Bound    harbor-jobservice   1Gi        RWO            slow           <unset>                 32h
harbor-registry                   Bound    harbor-registry     5Gi        RWO            slow           <unset>                 32h
```

### NFS側で準備

今回必要なのは、

- redis
- trivy
- db
- jobservice
- registry

の4つですね。

それぞれディレクトリを作っていきます。
私は、以下のようにしました。

```shell
$ ll /nfs/infra/harbor

total 36
drwxrwxr-x 7 yuito-it yuito-it      4096 Nov 18 00:26 ./
drwxrwxr-x 9 yuito-it unipro-infra  4096 Nov 17 23:44 ../
drwxrwxrwx 3 yuito-it yuito-it      4096 Nov 18 00:34 db/
drwxrwxrwx 2 yuito-it yuito-it     12288 Nov 19 09:00 jobservice/
drwxrwxrwx 2 yuito-it yuito-it      4096 Nov 19 09:05 redis/
drwxrwxrwx 3 yuito-it yuito-it      4096 Nov 18 08:40 registry/
drwxrwxrwx 4 yuito-it yuito-it      4096 Nov 18 00:34 trivy/
```

### PVを作る

ではこれを元に、PVを作っていきます。

```yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: harbor-jobservice
spec:
  capacity:
    storage: 1Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: slow
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /nfs/infra/harbor/jobservice
    server: nfs.unipro.infra
  claimRef:
    namespace: harbor
    name: harbor-jobservice
```

こんな感じで、それぞれPVのマニフェストファイルを作成していきます。
でもって、デプロイします。

これで、Podたちを再起動すれば完了です。

## アクセスして最初のイメージをPushする

まずはアクセスしてみましょう。

多分こんな感じになるんじゃないですかね。
(プロジェクト欄には何もないはず)

![projects.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342262/cdcf6223-0c0c-5d05-16c9-64c1778cabee.png)

### プロジェクトを作る

まず、プロジェクトを作成してみましょう。
Harborはプロジェクト単位でイメージを管理しています。
そのため、イメージ名は、`プロジェクト名/イメージ名`となります。

### ログイン

まず、Privateのプロジェクトにアクセスするには、Dockerでログインしなければなりません。
ですので、まず、Pushする端末で、

```shell
$ docker login HarborのURL
```

として、ログインしてください。

### Pushする

まず、ここを押して、Pushするためのコマンドを取得して、あとはいつも通りにPushするだけです。

![Screenshot 2024-11-19 at 20.49.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342262/32b191ba-452a-ec5d-0949-95105521c21f.png)

## K8sで使う

さて、K8sのDeploymentなりPodなりで使っていきましょうか。

### ロボットアカウントを作成する

流石にK8sにそのまま認証情報載せるのはちょっとなので、ロボットアカウントを作成します。
AdministrationのRobotAccountsから作成します。

有効期限等は適当でいいです。

権限設定ですが、以下の通りにしてください。

### 認証情報をデプロイする

先ほども書きましたが、場合によっては、ログインする必要がありますね。
例えば、プライベートなImageをPullするときなど...

K8sでは、`.dockerconf`タイプのSecretを作成し、割り当てることで認証できます。

ということで、まず、以下のSecretを作成しましょう。

```yaml
apiVersion: v1
stringData:
  .dockerconfigjson: |
    {"auths":{
      "HarborのURL":{
        "username":"robot$",
        "password":"シークレット",
        "auth":"「ユーザー名:シークレット」をbase64エンコードしたもの"
      }}}
kind: Secret
metadata:
  name: harbor-registry-secret
type: kubernetes.io/dockerconfigjson
```

### DeploymentやPodにアタッチする

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test
  labels:
    app: test
spec:
  selector:
    matchLabels:
      app: test
  template:
    metadata:
      labels:
        app: test
    spec:
      containers:
        - name: test
          image: harbor.unipro.infra/test/test:latest
          ports:
            - containerPort: 3000
      imagePullSecrets:
        - name: harbor-registry-secret # ここにシークレット名を入れる
```

こんな感じで記述します。
これにより、Pullするときにシークレットに指定した認証情報が使用されます。

## まとめ

今回はHarborを用いてローカルなContainerRegistryを構築して、K8s上で使うまでをやってみました。
次はHelmChartのRepoとして使ってみたいなぁと思ってます。
その時はまたQiitaに載せるので見てくださると幸いです！！

質問等あれば、コメント欄で教えてください...

### 最後に宣伝 デジタル創作サークルUniProject

私が構築したK8sはデジタル創作サークルUniProjectのサイトなどを動かしているK8sでした。
UniProjectでは、

- 音楽
- プログラミング
- デジ絵
- 3D
- デザイン

などなど、さまざまなことをしています。

参加特典として、**ProxmoxVEが無料で使えたり**、**K8sがあったり**します。
興味がある方はぜひ、Discordへ...

HP: https://uniproject-tech.net
Discord: https://discord.gg/nmjdqaZYVg