Skip to content

Commit 0c459a8

Browse files
Digest bugfix and refactoring
Switched to using crane.Digest to retrieve an image's digest. This simplifies the code and ensures consistency of all stored digests. These digests can also be used to pull their images, as opposed to the manifest digests retrieved via the v2 API Updated the README.md to more closely resemble the proposal in goharbor's community repo. Updated MAKEFILE Updated images.json to use a correct digest
1 parent a4189aa commit 0c459a8

File tree

6 files changed

+108
-122
lines changed

6 files changed

+108
-122
lines changed

README.md

Lines changed: 65 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,96 @@
1-
# Harbor Satellite — Brings Container Registries to the Edge
1+
# Proposal: `Harbor Satellite`
22

3-
The project aims to decentralize container registries for better accessibility to edge devices.
4-
Satellite registries can be used in stateful or stateless mode with the intention to function as a primary registry for the edge location, or as a fallback option if the central registry is unavailable. Satellite is crucial for operating in areas with limited or no internet, or when you need to distribute images to thousands or edge registries.
3+
Authors: Vadim Bauer / [Vad1mo](https://github.com/Vad1mo), Csaba Almasi, Philip Laine, David Huseby / [dhuseby](https://github.com/dhuseby), Roald Brunell / [OneFlyingBanana](https://github.com/OneFlyingBanana)
54

6-
## Primary Use Cases
5+
## Abstract
76

8-
- Overcome connectivity issues that affect software distribution to edge locations.
9-
- Mange distribution of containerized software to edge locations.
10-
- Managing hundreds or thousands of container registries at edge locations.
11-
- Works nicely with Kubernetes, yet can work without any container runtime, or as an edge bootstrap instance.
7+
Harbor Satellite aims to bring Harbor container registries to edge locations, ensuring consistent, available, and integrity-checked images for edge computing environments. This proposal outlines the development of a stateful, standalone satellite that can function as a primary registry for edge locations and as a fallback option if the central Harbor registry is unavailable.
128

13-
## Why
9+
## Background
10+
11+
In recent years, containers have extended beyond their traditional cloud environments, becoming increasingly prevalent in remote and edge computing contexts. These environments often lack reliable internet connectivity, posing significant challenges in managing and running containerized applications due to difficulties in fetching container images. To address this, the project aims to decentralize container registries, making them more accessible to edge devices. The need for a satellite that can operate independently, store images on disk, and run indefinitely with stored data is crucial for maintaining operations in areas with limited or no internet connectivity.
12+
13+
## Proposal
14+
15+
The proposed change is to develop "Harbor Satellite", an extension to the existing Harbor container registry. This extension will enable the operation of decentralized registries on edge devices.
16+
17+
Harbor Satellite will synchronize with the central Harbor registry, when Internet connectivity permits it, allowing it to receive and store images. This will ensure that even in environments with limited or unreliable internet connectivity, containerized applications can still fetch their required images from the local Harbor Satellite.
18+
19+
Harbor Satellite will also include a toolset enabling the monitoring and management of local decentralized registries.
20+
21+
## Non-Goals
22+
23+
T.B.D.
24+
25+
## Rationale
26+
27+
Deploying a complete Harbor instance on edge devices in poor/no coverage areas could prove problematic since :
1428

15-
Deploying a complete Harbor instance on edge devices in poor/no coverage areas could prove problematic, since :
1629
- Harbor wasn't designed to run on edge devices.(e.g. Multiple processes, no unattended mode)
1730
- Harbor could behave unexpectedly in poor/no connectivity environments.
1831
- Managing hundreds or thousands of container registries is not operationally feasible with Harbor
1932
- Harbor would be too similar to a simple registry mirror
2033

21-
Harbor Satellite aims to be resilient, lightweight and will be able to keep functioning independently of Harbor instances.
34+
Harbor Satellite aims to be resilient, lightweight and will be able to keep functioning independently from Harbor instances.
2235

23-
## How it Works
36+
## Compatibility
2437

25-
Harbor Satellite synchronizes with the central Harbor registry, when Internet connectivity permits it, allowing it to receive and store images. This will ensure that even in environments with limited or unreliable internet connectivity, containerized applications can still fetch their required images from the local Harbor Satellite.
38+
Compatibility with all container registries or edge devices can't be guaranteed.
2639

27-
Harbor Satellite will also include a toolset enabling the monitoring and management of local decentralized registries.
40+
## Implementation
2841

42+
### Overall Architecture
2943

30-
## Typical Use Cases
44+
Harbor Satellite, at its most basic, will run in a single container and will be divided in the following 2 components :
3145

32-
### Architecture
33-
Harbor Satellite, at its most basic, will run in a single container and will be divided into the following 2 components :
34-
35-
- **Satellite** : Is responsible for moving artifacts from upstream, identifying the source and reading the list of images that needs to be replicated. Satellite also modifies the container runtime configuration, so that the container runtime does not fetch images from remote.
36-
- **OCI Registry** : Is an embedded registry responsible for storing required OCI artifacts locally.
37-
- **Ground Control** : Is a component of Harbor and is responsible for serving a constructed list of images that need to be present on this edge location.
46+
- **Satellite** : Is responsible for moving artifacts from upstream (using Skopeo/Crane/Other), identifying the source and reading the list of images that needs to be replicated. Satellite will also be able to modify and manage the container runtimes. configuration in sync so that container runtime does not fetch images from remote.
47+
- **OCI Registry** : Is responsible for storing required OCI artifacts locally (using zotregistry or docker registry).
48+
- **Ground Control** : Is a component of Harbor and is responsible for serving a Harbor Satellite with the list of images it needs.
3849

3950
![Basic Harbor Satellite Diagram](docs/images/harbor-satellite-overview.svg)
4051

52+
<p align="center"><em>Basic Harbor Satellite Diagram</em></p>
53+
54+
### Specific Use Cases
4155

42-
### Replicating From a Remote Registry to the Edge Registry
56+
Harbor Satellite may be implemented following 1 or several of 3 different architectures depending on its use cases :
4357

44-
In this use case, the stateless satellite component will handle pulling images from a remote registry and then pushing them to the local OCI registry. This local registry will then be accessible to other local edge devices, who can pull required images directly from it.
58+
1. **Replicating from a remote registry to a local registry.**
59+
In this basic use case, the stateless satellite component will handle pulling images from a remote registry and then pushing them to the local OCI compliant registry. This local registry will then be accessible to other local edge devices who can pull required images directly from it.
60+
_(A direct access from edge device to the remote registry is still possible when network conditions permit it)._
61+
The satellite component may also handle updating container runtime configurations and fetching image lists from Ground Control, a part of Harbor.
62+
The stateful local regsitry will also need to handle storing and managing data on local volumes.
63+
A typical use case would work as follows :
64+
_In an edge computing environment where IoT devices are deployed to a location with limited or no internet connnectivity, these devices need to run containerised images but cannot pull from a central Harbor registry. A local Harbor Satellite instance can be deployed and take up this role while Internet connectivity is unreliable and distribute all required images. Once a reliable connection is re-established, the Harbor Satellite instance will be able to pull required images from its central Harbor registry and thus store up to date images locally._
4565

4666
![Use Case #1](docs/images/satellite_use_case_1.svg)
67+
<p align="center"><em>Use case #1</em></p>
4768

48-
### Replicating From a Remote Registry to an Edge Kubernetes Registry
69+
2. **Replicating from a remote regsitry to a local Spegel Registry**
70+
The stateless satellite component send pull instructions to Spegel instances running with each node of a Kubernetes cluster. The node will then directly pull images from a remote registry and share it with other local nodes, removing the need for each of them to individually pull an image from a remote registry.
71+
The network interfaces (boundaries) represented in this use case should and will be the same as those represented in use case #1
72+
A typical use case would work as follows :
73+
_In a larger scale edge computing environment with a significant amount of IoT devices needing to run containerised applications, a single local registry in might not be able to handle the increased amount of demands from edge devices. The solution is to deploy several registries to several nodes who are able to automatically replicate images across each other thanks to Spegel instances running together with each node. The Satellite component will use the same interface to instruct each node when, where and how to pull new images that need to be replicated across the cluster._
4974

50-
The stateless satellite component sends pull instructions to Spegel instances running on each Kubernetes node. The node container runtime will then directly pull images from a remote registry to its internal store. Building on Spegel images are now available for other local nodes, removing the need for each of them to individually pull an image from a remote registry.
51-
This use case only works in Kubernetes environments, the major advantage of such a setup compared to use case #1 is that it allows to operate a stateful registry on a stateless cluster. The only dependency satellite has is on spegel.
75+
![Use Case #2](docs/images/satellite_use_case_2.svg)
76+
<p align="center"><em>Use case #2</em></p>
5277

53-
![Use Case #1](docs/images/satellite_use_case_2.svg)
78+
3. **Proxying from a remote regsitry over the local registry**
79+
The stateless satellite component will be in charge of configuring the local OCI compliant registry, which will be running in proxy mode only. This local registry will then handle pulling necessary images from the remote registry and serving them up for use by local edge devices.
80+
A typical use case would work as follows :
81+
_When, for a number of possible different reasons, the remote registry side of the diagram would not be able to produce a list of images to push down to the Harbor Satellite, the Satellite would then act as a proxy and forward all requests from edge devices to the remote registry. This ensures the availability of necessary images without the need for a pre-compiled list of images_
5482

83+
![Use Case #3](docs/images/satellite_use_case_3.svg)
84+
<p align="center"><em>Use case #3</em></p>
5585

56-
### Proxying From a Remote Registry Over to the Edge Registry
57-
The stateless satellite component will be responsible for configuring the local OCI registry running in proxy mode and the configuration of the container runtime. This local registry is handing, image pulls from the remote registry and serving them up for use by local edge devices.
58-
In a highly dynamic environment where the remote registry operator or edge consumer cannot produce a list of images that need to be present on edge. the Satellite can also act as a remote proxy for edge devices. This ensures the availability of necessary images without the need for a pre-compiled list of images.
86+
### Container Runtime Configuration
5987

60-
![Use Case #1](docs/images/satellite_use_case_3.svg)
88+
In each of these use cases, we need to ensure that IoT edge devices needing to run containers will be able to access the registry and pull images from it. To solve this issue, we propose 4 solutions :
6189

90+
1. By using **containerd** or **CRI-O** and configuring a mirror within them.
91+
2. By setting up an **HTTP Proxy** to manage and optimize pull requests to the registry.
92+
3. By **directly referencing** the registry.
93+
4. By **directly referencing** the registry and using Kubernetes' mutating webhooks to point to the correct registry.
6294

6395
## Development
6496

@@ -67,4 +99,5 @@ The project is currently in active development. If you are interested in partici
6799
## Community, Discussion, Contribution, and Support
68100

69101
You can reach the Harbor community and developers via the following channels:
70-
- [#harbor-satellite on CNCF Slack ](https://cloud-native.slack.com/archives/C06NE6EJBU1)
102+
103+
- [#harbor-satellite on CNCF Slack](https://cloud-native.slack.com/archives/C06NE6EJBU1)

image-list/images.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"repository": "myproject",
66
"images": [
77
{
8-
"name": "album-server@sha256:23d6e6de0b63623e44a1ec59746682c77f197702ae77264eb287fa5119256f8a"
8+
"name": "album-server@sha256:71df27326a806ef2946ce502d26212efa11d70e4dcea06ceae612eb29cba398b"
99
},
1010
{
1111
"name": "album-server"

internal/replicate/replicate.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ func (r *BasicReplicator) Replicate(ctx context.Context, image string) error {
4040

4141
// TODO: Implement deletion of images from the local registry that are not present in the source registry
4242
// Probably use crane.Catalog to get a list of images in the local registry and compare to incoming image list
43+
// Then use crane.Delete to delete those images
4344

4445
source := getPullSource(image)
45-
fmt.Println("Source:", source)
4646

4747
if source != "" {
4848
CopyImage(source)
@@ -107,12 +107,7 @@ func CopyImage(imageName string) error {
107107
srcRef := imageName
108108
destRef := zotUrl + imageName
109109

110-
// Delete ./local-oci-layout directory if it already exists
111-
if err := os.RemoveAll("./local-oci-layout"); err != nil {
112-
return fmt.Errorf("failed to remove directory: %w", err)
113-
}
114-
115-
// Pull the image with additional flags and specify a destination directory
110+
// Pull the image and specify a destination directory
116111
srcImage, err := crane.Pull(srcRef)
117112
if err != nil {
118113
return fmt.Errorf("failed to pull image: %w", err)
@@ -126,7 +121,12 @@ func CopyImage(imageName string) error {
126121
return fmt.Errorf("failed to push image: %w", err)
127122
} else {
128123
fmt.Println("Image pushed successfully")
124+
}
129125

126+
// Delete ./local-oci-layout directory if it already exists
127+
// This is required because it is a temporary directory used by crane to pull and push images to and from
128+
if err := os.RemoveAll("./local-oci-layout"); err != nil {
129+
return fmt.Errorf("failed to remove directory: %w", err)
130130
}
131131

132132
return nil

internal/store/http-fetch.go

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import (
88
"io"
99
"net/http"
1010
"os"
11+
"strings"
1112

12-
v1 "github.com/opencontainers/image-spec/specs-go/v1"
13+
"github.com/google/go-containerregistry/pkg/authn"
14+
"github.com/google/go-containerregistry/pkg/crane"
1315
)
1416

1517
type RemoteImageList struct {
@@ -83,45 +85,24 @@ func (client *RemoteImageList) List(ctx context.Context) ([]Image, error) {
8385
}
8486

8587
func (client *RemoteImageList) GetDigest(ctx context.Context, tag string) (string, error) {
86-
// Construct the URL for fetching the manifest
87-
url := client.BaseURL + "/manifests/" + tag
88+
// Construct the image reference
89+
imageRef := fmt.Sprintf("%s:%s", client.BaseURL, tag)
90+
// Remove extra characters from the URL
91+
imageRef = imageRef[strings.Index(imageRef, "//")+2:]
92+
imageRef = strings.ReplaceAll(imageRef, "/v2", "")
8893

8994
// Encode credentials for Basic Authentication
9095
username := os.Getenv("HARBOR_USERNAME")
9196
password := os.Getenv("HARBOR_PASSWORD")
92-
auth := base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
93-
94-
// Create a new HTTP request
95-
req, err := http.NewRequest("GET", url, nil)
96-
if err != nil {
97-
return "", fmt.Errorf("failed to create request: %w", err)
98-
}
99-
100-
// Set the Authorization header
101-
req.Header.Set("Authorization", "Basic "+auth)
102-
103-
req.Header.Add("Accept", "application/vnd.oci.image.manifest.v1+json")
104-
105-
// Send the request
106-
httpClient := &http.Client{}
107-
resp, err := httpClient.Do(req)
108-
if err != nil {
109-
return "", fmt.Errorf("failed to fetch manifest: %w", err)
110-
}
111-
defer resp.Body.Close()
11297

113-
// Read the response body
114-
body, err := io.ReadAll(resp.Body)
98+
// Use crane.Digest to get the digest of the image
99+
digest, err := crane.Digest(imageRef, crane.WithAuth(&authn.Basic{
100+
Username: username,
101+
Password: password,
102+
}))
115103
if err != nil {
116-
return "", fmt.Errorf("failed to read response body: %w", err)
117-
}
118-
119-
// Unmarshal the JSON response
120-
var manifestResponse v1.Manifest
121-
if err := json.Unmarshal(body, &manifestResponse); err != nil {
122-
return "", fmt.Errorf("failed to unmarshal JSON response: %w", err)
104+
return "", fmt.Errorf("failed to fetch digest: %w", err)
123105
}
124106

125-
// Return the digest from the config section of the response
126-
return string(manifestResponse.Config.Digest), nil
107+
return digest, nil
127108
}

0 commit comments

Comments
 (0)