Red Hat Quay(w/ OpenShift Data Foundation)を使ってプライベートコンテナイメージレジストリを構築しよう

# はじめに
本記事では、OpenShiftクラスタへRed Hat Quayをdeployし、プライベートコンテナイメージレジストリを構築したいと思います。本記事のdeploy先のOpenShiftクラスタの構築については以下を参照して下さい。

https://qiita.com/ydo/items/874c7139119a17a4ecac

# Red Hat Quayとは
Red Hat Quayは、分散型コンテナイメージレジストリで、OpenShiftのOperator Hubを用いて簡単に構築できます。
QuayにはSaaS版とオンプレ版がありますが、今回はオンプレ版をdeployしセットアップしていきます。以下の赤帽ブログが参考になります。

https://rheb.hatenablog.com/entry/202002-quay

## 全体アーキテクチャ

Quayの全体アーキテクチャを以下に示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559968/041aaf46-b774-3190-8c8a-baf1eb333804.png)

QuayはPCからオンプレミスのサーバ、AWS、GCP、Azureなどのパブリッククラウドまで様々なインフラ環境へdeployすることができます。
マルチリージョン、マルチクラウドでレジストリのミラーリングを行なって、Geo Redundancyを構成したり、グローバルでのコンテナイメージの開発・共有を効率化できます。
また、コンテナイメージの脆弱性監視ツールである`Clair`と連携し、コンテナイメージのセキュリティも管理できます。

## ストレージ
Quayはバックエンドストレージとして主に以下のオブジェクトストレージを利用でき、全てのBlobをこのストレージへ格納します。
ローカルストレージとNFSはあくまで実験用途であり、オブジェクトストレージの使用が推奨されています。

- Ceph Rados RGW
- OpenStack Swift
- Red Hat OpenShift Data Foundation 4 (via NooBaa)
- Red Hat OpenShift Container Storage 3 (via NooBaa) 
- Supported Public Cloud Storage Types
- AWS S3
- Google Cloud Storage
- Azure Blob Storage

今回は勉強のために`OpenShift Data Foundation 4`をバックエンドストレージとしました。

## データベース
Quayの設定やメタデータ、ログはデータベースバックに格納されます。 尚、ログ格納先としてElasticSearchを利用することも可能です。
基本`PostgresSQL`を使うことになります。(MySQL・MariaDBも使用することは可能ですが、Quay 3.6で非推奨になりました)

## キャッシュ
BuilderログやQuayのチュートリアルの保存先として、`Redis`が使われます。
Redisが障害になっても大きな影響はありませんが、以下の項目が使用不可になります。
- Builderログのライブ表示
- チュートリアルの表示

## コンポーネント間の連携イメージ
今回構築するQuayのコンポーネント間の連携イメージを以下に示します。

OpenShiftクラスタがAWS上に構築されていますので、オブジェクトストレージはAWS S3、PostgreSQLのPersistent VolumeはAWS EBSを使用します。また、Multi-Cloud Object Gatewayの`NooBaa`を使用して、S3へアクセスする様にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559968/000b4781-6075-675d-ad55-622f62b76983.png)

# Quayの構築
## 1. OpenShift Data Foundationのdeploy
### 1-1. ストレージ用のノード追加

OpenShift Data Foundation(以降、ODF)をdeployするに当たり、以下のサイズのworkerノード3台をOpenShiftクラスタへ追加します。

|  項目  |  値  |
| ---- | ---- |
|  台数  |  3  |
|  CPU | 16   |
|  MEM | 32GB   | 
|  追加DISK  |  1024GB |
|インスタンスタイプ|c5a.4xlarge |

※スペックは以下の記事を参考にしました。

https://qiita.com/Yuhkih/items/89e0c76703a4f90bae67

OpenShiftクラスタへのノード追加は、Kubernetes Cluster APIの`Machine　API`を使います。
`Machine API`はカスタムリソースとして定義されており、`MachineSet`というリソースを使うことで、ReplicaSetとPodの関係の様にクラスタ内ノードを管理できます。また、クラスタ内のノードは`Machine`という形で定義されています。

https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.9/html/machine_management/index

既に登録済みのworkerノードのマニフェストを参考に、以下の流れでマニフェストをAvailability Zone毎に作成します。

### 1-2. マニフェストの雛形を取得

```bash
$ oc project openshift-machine-api
$ oc get machineset
NAME                                       DESIRED   CURRENT   READY   AVAILABLE   AGE
CLUSTER_NAME-INFRASTRUCTURE_ID-worker-ap-northeast-1a   1         1         1       1           24h
CLUSTER_NAME-INFRASTRUCTURE_ID-worker-ap-northeast-1c   1         1         1       1           24h
CLUSTER_NAME-INFRASTRUCTURE_ID-worker-ap-northeast-1d   1         1         1       1           24h


$ oc get machineset CLUSTER_NAME-INFRASTRUCTURE_ID-worker-ap-northeast-1a -o yaml > infra-ap-northeast-1a.yaml
```

### 1-3. マニフェストの修正
`Machine`名や`MachineSet`名は<クラスタ名>-<Infrastructure id>-<role>-<Availability Zone>の命名規則となります。
`Infrastructure id`は登録済みのworkerノードのものと揃えて下さい。

以下にマニフェスト内の変更箇所を⭐️で示します。このマニフェストをdeployしたいAvailability Zone分作ります。(置換すると楽です)

```yaml:infra-ap-northeast-1a.yaml
apiVersion: machine.openshift.io/v1beta1
kind: MachineSet
metadata:
  annotations:
    machine.openshift.io/GPU: "0"
    machine.openshift.io/memoryMb: ⭐️"32768"⭐️ # 32GB
    machine.openshift.io/vCPU: ⭐️"16"⭐️ # 16vCPU
  generation: 1
  labels:
    machine.openshift.io/cluster-api-cluster: ⭐️CLUSTER_NAME-INFRASTRUCTURE_ID-infra-ap-northeast-1a⭐️ #　roleをworkerからinfraへ変更
  name: ⭐️CLUSTER_NAME-INFRASTRUCTURE_ID-infra-ap-northeast-1a⭐️ #　roleをworkerからinfraへ変更
  namespace: openshift-machine-api
spec:
  replicas: 1
  selector:  
    matchLabels:
      machine.openshift.io/cluster-api-cluster: CLUSTER_NAME-INFRASTRUCTURE_ID
      machine.openshift.io/cluster-api-machineset: ⭐️CLUSTER_NAME-INFRASTRUCTURE_ID-infra-ap-northeast-1a⭐️ #　roleをworkerからinfraへ変更
  template:
    metadata:
      labels:
        machine.openshift.io/cluster-api-cluster: CLUSTER_NAME-INFRASTRUCTURE_ID
        machine.openshift.io/cluster-api-machine-role: infra
        machine.openshift.io/cluster-api-machine-type: infra
        machine.openshift.io/cluster-api-machineset: ⭐️CLUSTER_NAME-INFRASTRUCTURE_ID-infra-ap-northeast-1a⭐️ #　roleをworkerからinfraへ変更
    spec:
      metadata: {}
      providerSpec:
        value:
          ami:
            id: ami-0ba4e1ecb12d04732
          apiVersion: awsproviderconfig.openshift.io/v1beta1
          blockDevices:
          - ebs:
              encrypted: true
              iops: 0
              kmsKey:
                arn: ""
              volumeSize: ⭐️1024⭐️ #　1TB
              volumeType: gp2
          credentialsSecret:
            name: aws-cloud-credentials
          deviceIndex: 0
          iamInstanceProfile:
            id: CLUSTER_NAME-INFRASTRUCTURE_ID-worker-profile
          instanceType: c5a.4xlarge
          kind: AWSMachineProviderConfig
          metadata:
            creationTimestamp: null
          placement:
            availabilityZone: ap-northeast-1a
            region: ap-northeast-1
          securityGroups:
          - filters:
            - name: tag:Name
              values:
              - CLUSTER_NAME-INFRASTRUCTURE_ID
          subnet:
            filters:
            - name: tag:Name
              values:
              - CLUSTER_NAME-INFRASTRUCTURE_ID-private-ap-northeast-1a
          tags:
          - name: kubernetes.io/cluster/CLUSTER_NAME-INFRASTRUCTURE_ID-t79k7
            value: owned
          userDataSecret:
            name: worker-user-data
```

### 1-4. マニフェスト登録
`apply`して、構築完了するまで数分待ちます。

```
$ oc project openshift-machine-api
$ oc apply -f infra-ap-northeast-1a.yaml
$ oc get machine
NAME                                             PHASE         TYPE          REGION           ZONE              AGE
CLUSTER_NAME-INFRASTRUCTURE_ID-infra-ap-northeast-1a-9k9mt    Provisioned       c5a.4xlarge   ap-northeast-1   ap-northeast-1a   35m
...
CLUSTER_NAME-INFRASTRUCTURE_ID-master-0                       Running       m5.xlarge     ap-northeast-1   ap-northeast-1a   3h14m
CLUSTER_NAME-INFRASTRUCTURE_ID-master-1                       Running       m5.xlarge     ap-northeast-1   ap-northeast-1c   3h14m
CLUSTER_NAME-INFRASTRUCTURE_ID-master-2                       Running       m5.xlarge     ap-northeast-1   ap-northeast-1d   3h14m
CLUSTER_NAME-INFRASTRUCTURE_ID-worker-ap-northeast-1a-5pr7k   Running       m5.large      ap-northeast-1   ap-northeast-1a   3h9m
CLUSTER_NAME-INFRASTRUCTURE_ID-worker-ap-northeast-1c-6xj86   Running       m5.large      ap-northeast-1   ap-northeast-1c   3h9m
CLUSTER_NAME-INFRASTRUCTURE_ID-worker-ap-northeast-1d-m867j   Running       m5.large      ap-northeast-1   ap-northeast-1d   3h9m
```

`PHASE`が`Running`になることを確認します。

```
$ oc get machine

NAME                                             PHASE     TYPE          REGION           ZONE              AGE
CLUSTER_NAME-INFRASTRUCTURE_ID-infra-ap-northeast-1a-5zt96    Running   c5a.4xlarge   ap-northeast-1   ap-northeast-1a   24h
CLUSTER_NAME-INFRASTRUCTURE_ID-infra-ap-northeast-1a-lplcl    Running   c5a.4xlarge   ap-northeast-1   ap-northeast-1a   24h
CLUSTER_NAME-INFRASTRUCTURE_IDinfra-ap-northeast-1c-f47qz    Running   c5a.4xlarge   ap-northeast-1   ap-northeast-1c   24h
CLUSTER_NAME-INFRASTRUCTURE_ID-master-0                       Running   m5.xlarge     ap-northeast-1   ap-northeast-1a   25h
CLUSTER_NAME-INFRASTRUCTURE_ID-master-1                       Running   m5.xlarge     ap-northeast-1   ap-northeast-1c   25h
CLUSTER_NAME-INFRASTRUCTURE_ID-master-2                       Running   m5.xlarge     ap-northeast-1   ap-northeast-1d   25h
CLUSTER_NAME-INFRASTRUCTURE_ID-worker-ap-northeast-1a-dk466   Running   m5.large      ap-northeast-1   ap-northeast-1a   24h
CLUSTER_NAME-INFRASTRUCTURE_ID-worker-ap-northeast-1c-mwqn4   Running   m5.large      ap-northeast-1   ap-northeast-1c   24h
CLUSTER_NAME-INFRASTRUCTURE_IDworker-ap-northeast-1d-zrj7p   Running   m5.large      ap-northeast-1   ap-northeast-1d   24h
```

### 1-5. ノードラベルの変更
余計なリソースの消費を避けるために、作成したノードにユーザアプリケーションが稼働しない様、`infra`ラベルを付与しておきます。
`oc get nodes`の結果の`ROLES`の箇所が`infra,worker`となることを確認します。

```
$ oc label node NODE_NAME node-role.kubernetes.io/infra=

$ oc get nodes
NAME                                              STATUS   ROLES          AGE   VERSION
ip-10-0-137-150.ap-northeast-1.compute.internal   Ready    master         25h   v1.22.3+4dd1b5a
ip-10-0-150-193.ap-northeast-1.compute.internal   Ready    infra,worker   24h   v1.22.3+4dd1b5a
ip-10-0-154-161.ap-northeast-1.compute.internal   Ready    worker         24h   v1.22.3+4dd1b5a
ip-10-0-157-17.ap-northeast-1.compute.internal    Ready    infra,worker   24h   v1.22.3+4dd1b5a
ip-10-0-163-182.ap-northeast-1.compute.internal   Ready    infra,worker   24h   v1.22.3+4dd1b5a
ip-10-0-169-27.ap-northeast-1.compute.internal    Ready    master         25h   v1.22.3+4dd1b5a
ip-10-0-171-55.ap-northeast-1.compute.internal    Ready    worker         24h   v1.22.3+4dd1b5a
ip-10-0-206-171.ap-northeast-1.compute.internal   Ready    master         25h   v1.22.3+4dd1b5a
ip-10-0-221-245.ap-northeast-1.compute.internal   Ready    worker         24h   v1.22.3+4dd1b5a
```

InfraノードがODFのdeploy先ノードとして認識される様に、`cluster.ocs.openshift.io/openshift-storage=''`ラベルも付与しておきましょう。

```
$ oc label nodes NODE_NAME cluster.ocs.openshift.io/openshift-storage=''

$ oc get nodes -l cluster.ocs.openshift.io/openshift-storage -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}'
# ラベルの付与されたノードが出力されます。
```

それでは、ODFを構築していきます。


### 1-6. OperatorHubからOperatorをインストール

OpenShiftのWebコンソールにて、[Operator]-[OperatorHub]を選択し、`OpenShift Data Foundation`を選択します。

:::note info
OpenShift Container Storageは、v4からOpenShift Data Foundationへ統合されました。
:::

[インストールボタン]を押し、特に変更せずOperatorをインストールして下さい。


![1639461329582.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559968/07745dd9-180a-25b5-bd99-20b55b9b67ef.png)

ステータスが`Succeeded`になるまで待ちます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559968/61390ba8-6ebc-343e-cb6d-3afd7d10199d.png)


### 1-7. ストレージシステムの作成

Operatorがインストールされたら`Storage System`を作成します。
下記の画面が表示されていると思いますので、`StorageSystemの作成`をクリックして下さい。

![1639461381120.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559968/c258b19b-a343-6e91-eb07-df2468eab957.png)

今回は以下を設定しました。（ほぼデフォルトのままです）

|  項目  |  設定値  |
| ---- | ---- |
|  Storage Class  |  gp2  |
|  要求された要領 | 0.5TiB   |
| Select Nodes| 1-4.で作成したinfraノード 3台|
|  ネットワーク | デフォルト(SDN)   | 
|  データ暗号化  |  無 |

![1639461487497.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559968/f3f0fa98-5483-a749-b8aa-6461215a0090.png)

:::note info
Not Foundが表示されましたが、正常にdeployはされます。
:::

PodのSTATUSが`Running`となれば正常にdeploy完了です。

```bash
$ oc project openshift-storage
Now using project "openshift-storage" on server "https://api.CLUSTER_NAME-INFRASTRUCTURE_ID.CLUSTER_DOMAIN:6443".

$ oc get pods -n openshift-storage
NAME                                                              READY   STATUS      RESTARTS   AGE
csi-cephfsplugin-2f7cp                                            3/3     Running     0          13m
csi-cephfsplugin-4wx6h                                            3/3     Running     0          13m
csi-cephfsplugin-5xbmv                                            3/3     Running     0          13m
csi-cephfsplugin-provisioner-8cb8f558c-m6jkq                      6/6     Running     0          13m
csi-cephfsplugin-provisioner-8cb8f558c-svkgx                      6/6     Running     0          13m
csi-cephfsplugin-r4grt                                            3/3     Running     0          13m
csi-cephfsplugin-whk7x                                            3/3     Running     0          13m
csi-cephfsplugin-zrsl7                                            3/3     Running     0          13m
csi-rbdplugin-2jrgh                                               3/3     Running     0          13m
csi-rbdplugin-49hnk                                               3/3     Running     0          13m
csi-rbdplugin-5nczw                                               3/3     Running     0          13m
csi-rbdplugin-6z7km                                               3/3     Running     0          13m
csi-rbdplugin-7dlkh                                               3/3     Running     0          13m
csi-rbdplugin-kwdvt                                               3/3     Running     0          13m
csi-rbdplugin-provisioner-664c7b95b7-27xbv                        6/6     Running     0          13m
csi-rbdplugin-provisioner-664c7b95b7-dqz5c                        6/6     Running     0          13m
noobaa-core-0                                                     1/1     Running     0          9m17s
noobaa-db-pg-0                                                    1/1     Running     0          9m17s
noobaa-endpoint-797dd4455f-vlfw6                                  1/1     Running     0          7m46s
noobaa-operator-74b97f586d-m5qmt                                  1/1     Running     0          22m
ocs-metrics-exporter-784c5cb8b9-gsm8d                             1/1     Running     0          22m
ocs-operator-6bcc95b8df-mw957                                     1/1     Running     0          22m
odf-console-74d46c56b9-57s89                                      1/1     Running     0          22m
odf-operator-controller-manager-6869db8989-slk9p                  2/2     Running     0          22m
rook-ceph-crashcollector-885f0ffa37796f7c9305a7767c101c65-sxjjx   1/1     Running     0          10m
rook-ceph-crashcollector-eff1385f0036995238160716b9ad8e94-hvsrh   1/1     Running     0          10m
rook-ceph-crashcollector-fd67ecee1476e668b8991e9b8249861a-s9wjq   1/1     Running     0          10m
rook-ceph-mds-ocs-storagecluster-cephfilesystem-a-65d84867kbsjj   2/2     Running     0          9m29s
rook-ceph-mds-ocs-storagecluster-cephfilesystem-b-676cf6fd7mghz   2/2     Running     0          9m28s
rook-ceph-mgr-a-5db89bfd44-b62fz                                  2/2     Running     0          10m
rook-ceph-mon-a-76bc4bb647-95t79                                  2/2     Running     0          13m
rook-ceph-mon-b-6458778c59-hhrch                                  2/2     Running     0          11m
rook-ceph-mon-c-8dff84bff-cdbw4                                   2/2     Running     0          10m
rook-ceph-operator-5476ccb79b-q78sf                               1/1     Running     0          22m
rook-ceph-osd-0-7bc5c5f5f5-wblk5                                  2/2     Running     0          10m
rook-ceph-osd-1-fbdd66bf8-grjx5                                   2/2     Running     0          9m56s
rook-ceph-osd-2-67ffcf7449-w5sqv                                  2/2     Running     0          9m46s
rook-ceph-osd-prepare-ocs-deviceset-gp2-0-data-0574vp--1-f7wr8    0/1     Completed   0          10m
rook-ceph-osd-prepare-ocs-deviceset-gp2-1-data-08gftl--1-rlcxb    0/1     Completed   0          10m
rook-ceph-osd-prepare-ocs-deviceset-gp2-2-data-0txlpb--1-d95h5    0/1     Completed   0          10m
```


## 2. Red Hat Quayのdeploy

以下を参考に構築しました。

https://access.redhat.com/documentation/ja-jp/red_hat_quay/3.6/html-single/deploy_red_hat_quay_on_openshift_with_the_quay_operator/index

### 2-1. NooBaaリソースを定義

https://access.redhat.com/documentation/ja-jp/red_hat_quay/3.5/html/deploy_red_hat_quay_on_openshift_with_the_quay_operator/_create_a_standalone_object_gateway

```bash
$ oc project openshift-storage
Now using project "openshift-storage" on server "https://OPENSHIFT_API_ENDPOINT:6443".

$ vi noobaa.yaml 
```
```yaml:noobaa.yaml
apiVersion: noobaa.io/v1alpha1
kind: NooBaa
metadata:
  name: noobaa
  namespace: openshift-storage
spec:
 dbResources:
   requests:
     cpu: '0.1'
     memory: 1Gi
 dbType: postgres
 coreResources:
   requests:
     cpu: '0.1'
     memory: 1Gi
```

```bash
$ oc apply -f noobaa.yaml

$ oc get noobaa
NAME     MGMT-ENDPOINTS                  S3-ENDPOINTS                    IMAGE                                                                                                            PHASE   AGE
noobaa   ["https://IPADDR:31040"]   ["https://IPADDR:32376"]   registry.redhat.io/odf4/mcg-core-rhel8@sha256:6ce2ddee7aff6a0e768fce523a77c998e1e48e25d227f93843d195d65ebb81b9   Ready   24h
```

### 2-2.  OperatorHubからOperatorをインストール

OperatorHubにて`Red Hat Quay`を選択し、デフォルトのままインストールします。
今回は`stable-3.6`を選択しました。

![1639482130829.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559968/909b5010-733f-7b9a-f3b9-20c9df9ae995.png)

ステータスが`Succeeded`になるまで待ちます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559968/dfe229ad-c287-c8aa-e3d1-db7b29cacbfc.png)


### 2-3. QuayRegistryのdeploy

`quay`というプロジェクトを作成し、`QuayRegistry`をdeployします。
QuayRistryはQuayコンポーネントを管理・維持するカスタムリソースで、デフォルトでは全コンポーネント管理対象としています。

リソース定義後、自動的にPodが起動してきます。

```bash
$ oc new-project quay

$ vi quay-registry.yaml 
```
```yaml:quay-registry.yaml
apiVersion: quay.redhat.com/v1
kind: QuayRegistry
metadata:
  name: my-registry
```

```bash
$ oc apply -f quay-registry.yaml

$ oc get pods
NAME                                            READY   STATUS      RESTARTS        AGE
my-registry-clair-app-8999d46b4-4sz42           1/1     Running     0               2m58s
my-registry-clair-app-8999d46b4-xxfck           1/1     Running     0               3m19s
my-registry-clair-postgres-55f64f44f7-gdkf4     1/1     Running     1 (3m57s ago)   4m28s
my-registry-quay-app-58b6b58d89-49v47           1/1     Running     0               3m18s
my-registry-quay-app-58b6b58d89-sww2f           1/1     Running     0               3m19s
my-registry-quay-app-upgrade--1-z6qf8           0/1     Completed   0               3m26s
my-registry-quay-config-editor-dcd7758f-ht8kt   1/1     Running     0               3m19s
my-registry-quay-database-7c78b77d68-nks92      1/1     Running     0               4m28s
my-registry-quay-mirror-6b7b95985c-htk5g        1/1     Running     0               3m15s
my-registry-quay-mirror-6b7b95985c-kwffb        1/1     Running     0               3m15s
my-registry-quay-postgres-init--1-r8hwj         0/1     Completed   0               3m19s
my-registry-quay-redis-7959fc965-wtjvw          1/1     Running     0               4m28s
```

Quay Registryの接続先ドメインを確認します。

```bash
$ oc get route
NAME                             HOST/PORT                                                                      PATH   SERVICES                         PORT   TERMINATION     WILDCARD
my-registry-quay                 QUAY_REGISTRY_NAME-PROJECT_NAME-quay.apps.CLUSTER_DOMEIN                        my-registry-quay-app             http   edge/Redirect   None
my-registry-quay-builder         QUAY_REGISTRY_NAME-PROJECT_NAME-builder-quay.apps.CLUSTER_DOMEIN                my-registry-quay-app             grpc   edge/Redirect   None
my-registry-quay-config-editor   QUAY_REGISTRY_NAME-PROJECT_NAME-config-editor-quay.apps.CLUSTER_DOMEIN          my-registry-quay-config-editor   http   edge/Redirect   None
```

`https://QUAY_REGISTRY_NAME-PROJECT_NAME-quay.apps.CLUSTER_DOMEIN`へアクセスすると、下記の画面が表示されます。
アカウントを作成し、コンテナイメージレジストリとして利用できる様になります。

![1639482851585.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559968/e3c5eadf-52d1-f376-fa0d-6026050eedcc.png)


## 3. Let’s EncryptのTLS証明書の使用

Quayは、デフォルトで自己証明書を使用する様になっています。
TLS証明書は[この手順](https://access.redhat.com/documentation/ja-jp/red_hat_quay/3.3/html/manage_red_hat_quay/adding-tls-certificates-to-the-quay-enterprise-container)で追加できますが、ここでは、`openshift-acme-operator`を使用して、QuayRegistryのTLS証明書をLet's Encryptにて自動発行・管理出来るようにしたいと思います。

## 3-1. openshift-acme-operatorをdeploy
`openshift-acme-operator`を使うことで、Routeリソースの`metadata.annotations`へ`kubernetes.io/tls-acme: 'true'`を追加するだけで証明書を有効化できます。

https://github.com/tnozicka/openshift-acme

### 3-1. Project作成

```bash
$ oc new-project prod-letsencrypt
```

### 3-2. Operatorのdeploy

GitHubから`ClusterRole`、`ServiceAccount`、`Deployment`、`Operator`をdeployします。
PodのSTATUSが`Running`になるのを待ちます。

```bash
$ oc create -fhttps://raw.githubusercontent.com/tnozicka/openshift-acme/master/deploy/cluster-wide/{clusterrole,serviceaccount,issuer-letsencrypt-live,deployment}.yaml

clusterrole.rbac.authorization.k8s.io/openshift-acme created
serviceaccount/openshift-acme created
configmap/letsencrypt-live created
deployment.apps/openshift-acme created

$ oc get pods
NAME                              READY   STATUS    RESTARTS   AGE
openshift-acme-54496d7cc7-6lhzm   1/1     Running   0          21h
openshift-acme-54496d7cc7-pmf9v   1/1     Running   0          21h

$ oc adm policy add-cluster-role-to-user openshift-acme -z openshift-acme
```

### 3-3. QuayRegistry APIによるRouteの管理を外す

RouteがQuayRegistry APIの管理対象になっていると、Routeのマニフェストを変更しても元に戻されてしまうので、以下の流れで管理対象から外します。

```bash
$ oc edit QuayRegistry
...
spec:
  components:
  - kind: clair
    managed: true
  ...
  - kind: route
    managed: false ⭐️ # true→false　に変更
  ...
```

`my-registry-quay`のRouteの`metadata.annotations`に`kubernetes.io/tls-acme: 'true'`を付与します。

```bash
$ oc project quay

$ oc edit route my-registry-quay

kind: Route
apiVersion: route.openshift.io/v1
metadata:
  annotations:
    kubernetes.io/tls-acme: 'true'　⭐️
```

自動的に、Routeの`metadata.annotations.acme.openshift.io/status:`が付与され、プロビジョニングの状態が更新されます。
数分待つとTLS証明書が交付され、`orderStatus: valid`となり、httpsアクセスできる様になります。

```bash
$ oc get route my-registry-quay  -o yaml
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  annotations:
    acme.openshift.io/status: |
      provisioningStatus:
        earliestAttemptAt: "2021-12-14T14:03:01.685569846Z"
        orderStatus: valid
        orderURI: https://acme-v02.api.letsencrypt.org/acme/order/320937410/47241067660
        startedAt: "2021-12-14T14:03:01.685569846Z"
    kubernetes.io/tls-acme: "true"
 ...

```

### 3-4. 接続確認

```bash
$ docker push QUAY_REGISTRY_NAME-PROJECT_NAME-quay.apps.CLUSTER_DOMEIN
Username: # QUAYのWebコンソールで設定したユーザ名
Password: # QUAYのWebコンソールで設定したユーザ名

Login Succeeded
```

以上で、Red Hat Quayを使ってプライベートコンテナレジストリを構築しました。


# 【参考】 Quayのconfig.yamlについて
QuayRegistryのマニフェストでは、Quayコンポーネントの管理要否の選択の他、Quayの設定パラメータを纏めた`config.yaml`を指定することができます。

`config.yaml`はSecretに登録し、QuayRegistryのマニフェストの`spec.configBundleSecret`へ作成したSecretを指定します。

```bash
apiVersion: quay.redhat.com/v1
kind: QuayRegistry
metadata:
  name: my-registry
spec:
  components:
    - kind: clair
      managed: true
    - kind: redis
      managed: true
    - kind: horizontalpodautoscaler
      managed: true
    - kind: objectstorage
      managed: true
    - kind: route
      managed: true
    - kind: mirror
      managed: true
    - kind: monitoring
      managed: true
    - kind: tls
      managed: true
    - kind: postgres
      managed: true
  configBundleSecret: my-registry-config-bundle-vtfn9
```

例として`config.yaml`記述方法を以下に示します。詳細は公式ドキュメントを参照して下さい。

https://access.redhat.com/documentation/ja-jp/red_hat_quay/3.5/html-single/manage_red_hat_quay/index#_editing_the_config_yaml_file_to_modify_red_hat_quay

```bash
$ vi config.yaml
ALLOW_PULLS_WITHOUT_STRICT_LOGGING: false
AUTHENTICATION_TYPE: Database
DEFAULT_TAG_EXPIRATION: 2w
ENTERPRISE_LOGO_URL: /static/img/quay-horizontal-color.svg
FEATURE_BUILD_SUPPORT: false
FEATURE_DIRECT_LOGIN: true
FEATURE_MAILING: false
REGISTRY_TITLE: Quay
REGISTRY_TITLE_SHORT: Quay
SETUP_COMPLETE: true
TAG_EXPIRATION_OPTIONS:
- 2w
TEAM_RESYNC_STALE_TIME: 60m
TESTING: false

$ oc create secret generic --from-file config.yaml=./config.yaml config-bundle-secret
```