Files
K3S/addons/harbor/role/tasks/main.yml
Sergey Antropoff 5dc0fbcd3a feat: harbor — proxy cache зеркалирование + tag retention policy
Proxy cache (harbor_proxy_cache_enabled: true):
- Автоматически создаёт registry endpoints + proxy cache проекты для:
  docker.io, gcr.io, quay.io, ghcr.io, registry.k8s.io, mcr.microsoft.com, public.ecr.aws
- При pull образа через harbor.example.com/<registry>/<image> он кэшируется
- Реализовано через alpine:3.19 + curl + jq Kubernetes Job (вызывает Harbor REST API изнутри кластера)

Tag retention (harbor_retention_enabled: true, harbor_retention_max_tags: 3):
- Политика "latestPushedN=3" применяется ко ВСЕМ проектам (включая proxy cache)
- Пропускает проекты с уже существующей политикой (idempotent)
- Запуск: ежедневно в 03:00 UTC (cron schedule в Harbor)

Механизм: Job запускается после Helm install, достучивается до harbor-core по
internal service DNS, ждёт API готовности (40 попыток × 15 сек = 10 мин max).
2026-04-25 11:54:43 +03:00

160 lines
6.1 KiB
YAML

---
- name: Add Harbor Helm repo
kubernetes.core.helm_repository:
name: harbor
repo_url: "{{ harbor_chart_repo }}"
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Fetch latest Harbor chart version
ansible.builtin.command: helm search repo harbor/harbor --output json
register: _harbor_chart_search
changed_when: false
when: harbor_version == ""
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Set effective Harbor chart version
ansible.builtin.set_fact:
_harbor_chart_version: >-
{{ harbor_version if harbor_version != '' else
(_harbor_chart_search.stdout | from_json)[0].version }}
- name: Show Harbor chart version that will be installed
ansible.builtin.debug:
msg: "Устанавливаю Harbor chart {{ _harbor_chart_version }}"
- name: Create dedicated PostgreSQL user and database for Harbor
kubernetes.core.k8s:
state: present
definition:
apiVersion: batch/v1
kind: Job
metadata:
name: harbor-pg-provision
namespace: "{{ postgresql_namespace | default('postgresql') }}"
spec:
ttlSecondsAfterFinished: 300
template:
spec:
restartPolicy: OnFailure
containers:
- name: psql
image: postgres:16-alpine
command:
- /bin/sh
- -c
- |
PGPASSWORD="$ADMIN_PASS" psql -h "$HOST" -U postgres -c "
DO \$\$
BEGIN
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${DB_USER}') THEN
CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASS}';
END IF;
END \$\$;
" &&
PGPASSWORD="$ADMIN_PASS" psql -h "$HOST" -U postgres -tc \
"SELECT 1 FROM pg_database WHERE datname = '${DB_NAME}'" \
| grep -q 1 || \
PGPASSWORD="$ADMIN_PASS" psql -h "$HOST" -U postgres -c \
"CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};"
env:
- name: HOST
value: "{{ harbor_db_host }}"
- name: ADMIN_PASS
value: "{{ harbor_postgresql_admin_password }}"
- name: DB_USER
value: "{{ harbor_db_username }}"
- name: DB_PASS
value: "{{ harbor_db_password }}"
- name: DB_NAME
value: "{{ harbor_db_name }}"
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
when: harbor_database_type == 'external'
- name: Wait for Harbor PostgreSQL provision Job to complete
ansible.builtin.command: >
k3s kubectl -n {{ postgresql_namespace | default('postgresql') }}
wait job/harbor-pg-provision
--for=condition=complete --timeout=120s
changed_when: false
when: harbor_database_type == 'external'
- name: Template Harbor values
ansible.builtin.template:
src: harbor-values.yaml.j2
dest: /tmp/harbor-values.yaml
mode: '0644'
- name: Install Harbor via Helm
kubernetes.core.helm:
name: harbor
chart_ref: harbor/harbor
chart_version: "{{ _harbor_chart_version }}"
release_namespace: "{{ harbor_namespace }}"
create_namespace: true
wait: true
timeout: "15m0s"
values_files:
- /tmp/harbor-values.yaml
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Template Harbor configuration Job (proxy cache + retention)
ansible.builtin.template:
src: harbor-configure-job.yaml.j2
dest: /tmp/harbor-configure-job.yaml
mode: '0644'
when: harbor_proxy_cache_enabled | bool or harbor_retention_enabled | bool
- name: Delete previous Harbor configuration Job (если было)
ansible.builtin.command: >
k3s kubectl -n {{ harbor_namespace }} delete job harbor-configure --ignore-not-found=true
changed_when: false
when: harbor_proxy_cache_enabled | bool or harbor_retention_enabled | bool
- name: Apply Harbor configuration Job
ansible.builtin.command: k3s kubectl apply -f /tmp/harbor-configure-job.yaml
changed_when: true
when: harbor_proxy_cache_enabled | bool or harbor_retention_enabled | bool
- name: Wait for Harbor configuration Job to complete
ansible.builtin.command: >
k3s kubectl -n {{ harbor_namespace }}
wait job/harbor-configure
--for=condition=complete --timeout=600s
changed_when: false
when: harbor_proxy_cache_enabled | bool or harbor_retention_enabled | bool
- name: Show Harbor configuration Job logs
ansible.builtin.command: >
k3s kubectl -n {{ harbor_namespace }} logs
-l job-name=harbor-configure --tail=50
register: _harbor_configure_logs
changed_when: false
failed_when: false
when: harbor_proxy_cache_enabled | bool or harbor_retention_enabled | bool
- name: Harbor configuration output
ansible.builtin.debug:
msg: "{{ _harbor_configure_logs.stdout_lines }}"
when: harbor_proxy_cache_enabled | bool or harbor_retention_enabled | bool
- name: Show Harbor access info
ansible.builtin.debug:
msg:
- "Harbor установлен в namespace: {{ harbor_namespace }}"
- "URL: http{{ 's' if harbor_ingress_tls else '' }}://{{ harbor_ingress_host }}"
- "Логин: admin / Пароль: {{ harbor_admin_password }}"
- "Docker login: docker login {{ harbor_ingress_host }} -u admin"
- "БД: {{ harbor_database_type }}{{ ' (PostgreSQL ' + harbor_db_host + ')' if harbor_database_type == 'external' else '' }}"
- ""
- "Proxy cache pull (образ кэшируется в Harbor автоматически):"
- " docker pull {{ harbor_ingress_host }}/dockerhub/library/nginx:latest"
- " docker pull {{ harbor_ingress_host }}/gcr/google-containers/pause:3.9"
- " docker pull {{ harbor_ingress_host }}/ghcr/owner/image:tag"
- " docker pull {{ harbor_ingress_host }}/k8s-registry/kube-apiserver:v1.30.0"
- ""
- "Retention policy: хранить последние {{ harbor_retention_max_tags }} тега, запуск в 03:00 UTC"