[Infrastructure as Code (IaC) Pulumi] Use Pulumi kubernetes (K8S) Helm Chart to deploy Harbor

Harbor

Harbor is an open source registry that secures artifacts with policies and role-based access control, ensures images are scanned and free from vulnerabilities, and signs images as trusted. Harbor, a CNCF Graduated project, delivers compliance, performance, and interoperability to help you consistently and securely manage artifacts across cloud native compute platforms like Kubernetes and Docker.

This article is about how to use Pulumi, kubernetes (K8S) provider, Helm Chart and TypeScript SDK to deploy Harbor within Kubernetes (K8S).

Prerequisites

Usage

Pulumi New

Create the workspace directory.

1
2
3
$ mkdir -p col-example-pulumi-typescript-harbor

$ cd col-example-pulumi-typescript-harbor

Pulumi login into local file system.

1
2
3
$ pulumi login file://.
Logged in to cloudolife as cloudolife (file://.)
or visit https://pulumi.com/docs/reference/install/ for manual instructions and release notes.

Pulumi new a project with kubernetes-typescript SDK.

1
$ pulumi new kubernetes-typescript

The above command will create some files within the current directory.

1
2
3
4
5
6
7
8
tree . -L 1
.
├── node_modules/
├── package.json
├── package.json.lock
├── Pulumi.dev.yaml
├── Pulumi.yaml
└── main.ts

Install js-yaml package to load and parse yaml file.

1
$ npm i js-yaml

Pulumi Configuration

Configure Kubernetes

By default, Pulumi will look for a kubeconfig file in the following locations, just like kubectl:

  • The environment variable: $KUBECONFIG,

  • Or in current user’s default kubeconfig directory: ~/.kube/config

If the kubeconfig file is not in either of these locations, Pulumi will not find it, and it will fail to authenticate against the cluster. Set one of these locations to a valid kubeconfig file, if you have not done so already.

Configure Values.yaml

Edit values.yaml and replace content within {{ }}.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# values.yaml

# harbor-helm/values.yaml at master · goharbor/harbor-helm
# https://github.com/goharbor/harbor-helm/blob/master/values.yaml

expose:

ingress:
hosts:
core: {{ .Values.ingress.host.core }}
notary: {{ .Values.ingress.host.notary }}

annotations:
kubernetes.io/ingress.class: "nginx"

# The external URL for Harbor core service. It is used to
# 1) populate the docker/helm commands showed on portal
# 2) populate the token service URL returned to docker/notary client
#
# Format: protocol://domain[:port]. Usually:
# 1) if "expose.type" is "ingress", the "domain" should be
# the value of "expose.ingress.hosts.core"
# 2) if "expose.type" is "clusterIP", the "domain" should be
# the value of "expose.clusterIP.name"
# 3) if "expose.type" is "nodePort", the "domain" should be
# the IP address of k8s node
#
# If Harbor is deployed behind the proxy, set it as the URL of proxy
externalURL: {{ .Values.externalURL }}

# The initial password of Harbor admin. Change it from portal after launching Harbor
harborAdminPassword: {{ .Values.harborAdminPassword }}

registry:
# If true, the registry returns relative URLs in Location headers. The client is responsible for resolving the correct URL.
relativeurls: true

See and modify main.ts file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// main.ts

import * as pulumi from "@pulumi/pulumi";

import * as k8s from "@pulumi/kubernetes";

const yaml = require('js-yaml');
const fs = require('fs');

const nameHarbor = "harbor"

// kubernetes.core/v1.Namespace | Pulumi
// https://www.pulumi.com/docs/reference/pkg/kubernetes/core/v1/namespace/
const namespaceHarbor = new k8s.core.v1.Namespace(nameHarbor, {
metadata: {
name: nameHarbor,
},
})

const values = yaml.safeLoad(fs.readFileSync("./values.yaml", 'utf8'))

const charNameHarbor = "harbor"

// kubernetes.helm.sh/v3.Chart | Pulumi
// https://www.pulumi.com/docs/reference/pkg/kubernetes/helm/v3/chart/

// goharbor/harbor-helm: The helm chart to deploy Harbor
// https://github.com/goharbor/harbor-helm
// harbor 1.7.2 · helm/harbor
// https://artifacthub.io/packages/helm/harbor/harbor
const charHarbor = new k8s.helm.v3.Chart(charNameHarbor, {
chart: charNameHarbor,
version: "1.7.2",
fetchOpts:{
repo: "https://helm.goharbor.io",
},
namespace: namespaceHarbor.metadata.name,
values: values,
});

Pulumi Up

Run pulumi up to create the namespace and pods.

1
$ pulumi up

See pods about harbor.

1
2
3
4
5
6
7
8
9
10
11
12
$ kubectl get pods -n harbor
NAME READY STATUS RESTARTS AGE
harbor-chartmuseum-76bb897f77-s7lns 1/1 Running 0 132m
harbor-core-7956d9df75-vmdjr 1/1 Running 2 132m
harbor-database-0 1/1 Running 0 127m
harbor-jobservice-5d46c74d95-cbq7h 1/1 Running 1 132m
harbor-notary-server-5649bf85cd-rlllq 1/1 Running 4 129m
harbor-notary-signer-5d9c87fccc-47mq9 1/1 Running 3 129m
harbor-portal-7697df754-kffm6 1/1 Running 0 129m
harbor-redis-0 1/1 Running 0 129m
harbor-registry-7757657f9b-lj7nj 2/2 Running 0 132m
harbor-trivy-0 1/1 Running 0 128m

Pulumi Destroy

Destroy all resources created by Pulumi.

1
$ pulumi destroy

Advance

Use SelfSigned or other Certification

First, create a Kubernetes Secret from your certificate,

1
2
3
$ kubectl create secret tls my-tls-secret \
--cert=path/to/cert/file \
--key=path/to/key/file

Then, edit values.yaml and replace content within {{ }}.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# values.yaml

# harbor-helm/values.yaml at master · goharbor/harbor-helm
# https://github.com/goharbor/harbor-helm/blob/master/values.yaml

expose:

tls:

# The source of the tls certificate. Set it as "auto", "secret"
# or "none" and fill the information in the corresponding section
# 1) auto: generate the tls certificate automatically
# 2) secret: read the tls certificate from the specified secret.
# The tls certificate can be generated manually or by cert manager
# 3) none: configure no tls certificate for the ingress. If the default
# tls certificate is configured in the ingress controller, choose this option
certSource: secret

secret:
# The name of secret which contains keys named:
# "tls.crt" - the certificate
# "tls.key" - the private key
secretName: my-tls-secret
# The name of secret which contains keys named:
# "tls.crt" - the certificate
# "tls.key" - the private key
# Only needed when the "expose.type" is "ingress".
notarySecretName: my-tls-secret

ingress:
hosts:
core: {{ .Values.ingress.host.core }}
notary: {{ .Values.ingress.host.notary }}

annotations:
kubernetes.io/ingress.class: "nginx"

# The external URL for Harbor core service. It is used to
# 1) populate the docker/helm commands showed on portal
# 2) populate the token service URL returned to docker/notary client
#
# Format: protocol://domain[:port]. Usually:
# 1) if "expose.type" is "ingress", the "domain" should be
# the value of "expose.ingress.hosts.core"
# 2) if "expose.type" is "clusterIP", the "domain" should be
# the value of "expose.clusterIP.name"
# 3) if "expose.type" is "nodePort", the "domain" should be
# the IP address of k8s node
#
# If Harbor is deployed behind the proxy, set it as the URL of proxy
externalURL: {{ .Values.externalURL }}

# The initial password of Harbor admin. Change it from portal after launching Harbor
harborAdminPassword: {{ .Values.harborAdminPassword }}

# The name of the secret which contains key named "ca.crt". Setting this enables the
# download link on portal to download the certificate of CA when the certificate isn't
# generated automatically
caSecretName: my-tls-secret

registry:
# If true, the registry returns relative URLs in Location headers. The client is responsible for resolving the correct URL.
relativeurls: true

References

[1] Harbor docs | Deploying Harbor with High Availability via Helm - https://goharbor.io/docs/2.3.0/install-config/harbor-ha-helm/

[2] Harbor - https://goharbor.io/

[3] goharbor/harbor-helm: The helm chart to deploy Harbor - https://github.com/goharbor/harbor-helm

[4] harbor-helm/values.yaml at master · goharbor/harbor-helm - https://github.com/goharbor/harbor-helm/blob/master/values.yaml

[5] Kubernetes Getting Started | Pulumi - https://www.pulumi.com/docs/get-started/kubernetes/

[6] Pulumi - Modern Infrastructure as Code - https://www.pulumi.com/

[7] Kubernetes - https://kubernetes.io/

[8] TypeScript: Typed JavaScript at Any Scale. - https://www.typescriptlang.org/

[9] Helm - https://helm.sh/