harbor offlie 설치 상세 2

진웅·2025년 8월 19일

harbor

목록 보기
2/5

Harbor 2.13.2 + Podman 대용량 클러스터 설치 가이드

사전 준비사항

시스템 요구사항 (대용량 기준)

  • CPU: 16 cores 이상
  • Memory: 32GB 이상
  • Storage: 1TB 이상 (SSD 권장)
  • Network: 10Gbps 이상
  • OS: CentOS 8/RHEL 8/Rocky Linux 8 이상

필수 패키지 설치

# 기본 패키지 설치
sudo dnf install -y wget curl openssl tar gzip unzip

# Podman Compose 설치 확인
sudo dnf install -y podman-compose

# 또는 Docker Compose 호환 설치
sudo dnf install -y python3-pip
pip3 install docker-compose

1. 시스템 최적화 설정

커널 파라미터 튜닝

# /etc/sysctl.conf 편집
cat >> /etc/sysctl.conf <<EOF
# 네트워크 최적화
net.core.rmem_max = 268435456
net.core.wmem_max = 268435456
net.core.rmem_default = 268435456
net.core.wmem_default = 268435456
net.core.netdev_max_backlog = 5000
net.core.somaxconn = 32768
net.ipv4.tcp_rmem = 4096 87380 268435456
net.ipv4.tcp_wmem = 4096 65536 268435456
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_max_syn_backlog = 8192

# 파일 디스크립터 최적화
fs.file-max = 1048576
fs.nr_open = 1048576

# 메모리 최적화
vm.max_map_count = 262144
vm.swappiness = 10
EOF

sysctl -p

시스템 제한 설정

# /etc/security/limits.conf 편집
cat >> /etc/security/limits.conf <<EOF
* soft nofile 1048576
* hard nofile 1048576
* soft nproc 1048576
* hard nproc 1048576
* soft memlock unlimited
* hard memlock unlimited
EOF

방화벽 설정

# 필요한 포트 열기
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --permanent --add-port=4443/tcp
sudo firewall-cmd --reload

2. 자체 서명 인증서 생성

대용량 환경용 인증서 생성 스크립트

#!/bin/bash
# create-harbor-ssl-cert.sh

# 실제 서버 IP로 변경
SERVER_IP="192.168.1.100"
CERT_DIR="/opt/harbor/certs"

echo "Harbor 대용량 환경용 SSL 인증서 생성..."

# 인증서 디렉토리 생성
mkdir -p ${CERT_DIR}
cd ${CERT_DIR}

# CA 개인키 생성 (4096bit)
openssl genrsa -out ca.key 4096

# CA 인증서 생성 (10년 유효)
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
  -subj "/C=KR/ST=Seoul/L=Seoul/O=Harbor-Registry/OU=IT/CN=Harbor-CA"

# 서버 개인키 생성 (4096bit)
openssl genrsa -out harbor.key 4096

# 인증서 설정 파일 생성
cat > harbor.conf <<EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no

[req_distinguished_name]
C=KR
ST=Seoul
L=Seoul
O=Harbor-Registry
OU=IT
CN=${SERVER_IP}

[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
IP.1 = ${SERVER_IP}
DNS.1 = harbor
DNS.2 = localhost
DNS.3 = *.local
EOF

# CSR 생성
openssl req -new -key harbor.key -out harbor.csr -config harbor.conf

# 서버 인증서 생성 (10년 유효)
openssl x509 -req -in harbor.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out harbor.crt -days 3650 -extensions v3_req -extfile harbor.conf

# 인증서 체인 생성
cat harbor.crt ca.crt > harbor-fullchain.crt

# 파일 정리
rm harbor.csr harbor.conf

# 권한 설정
chmod 644 *.crt
chmod 600 *.key
chown -R root:root ${CERT_DIR}

echo "✓ SSL 인증서 생성 완료"
echo "Certificate: ${CERT_DIR}/harbor.crt"
echo "Full Chain: ${CERT_DIR}/harbor-fullchain.crt"
echo "Private Key: ${CERT_DIR}/harbor.key"
echo "CA Certificate: ${CERT_DIR}/ca.crt"

3. Harbor 2.13.2 설치

Harbor 다운로드 및 압축 해제

# 설치 디렉토리 생성
mkdir -p /opt/harbor
cd /opt

# Harbor 오프라인 설치 파일 압축 해제
tar xvf harbor-offline-installer-v2.13.2.tgz
mv harbor /opt/
cd /opt/harbor

대용량 클러스터용 harbor.yml 설정

# harbor.yml.tmpl을 복사하여 설정 파일 생성
cp harbor.yml.tmpl harbor.yml

# harbor.yml 편집
cat > harbor.yml <<EOF
# Harbor 서버 IP (실제 IP로 변경)
hostname: 192.168.1.100

# HTTP 설정 (리다이렉션용)
http:
  port: 80

# HTTPS 설정
https:
  port: 443
  certificate: /opt/harbor/certs/harbor-fullchain.crt
  private_key: /opt/harbor/certs/harbor.key

# 관리자 패스워드 (강력한 패스워드로 변경)
harbor_admin_password: HarborAdmin2024!

# 데이터베이스 설정 (대용량 최적화)
database:
  password: PostgresPassword2024!
  max_idle_conns: 100          # 기본 2 → 100
  max_open_conns: 2000         # 기본 100 → 2000
  conn_max_lifetime: 5m
  conn_max_idle_time: 0

# 데이터 볼륨 (대용량 스토리지 마운트 포인트)
data_volume: /harbor-data

# Redis 설정
redis:
  host: redis
  port: 6379
  password: RedisPassword2024!
  registry_db_index: 1
  jobservice_db_index: 2
  trivy_db_index: 5
  idle_timeout_seconds: 30

# Trivy 보안 스캔 설정 (오프라인 최적화)
trivy:
  ignore_unfixed: false
  skip_update: false
  offline_scan: true
  security_check: vuln,config,secret
  insecure: false
  skip_java_db_update: false
  timeout: 5m0s

# 작업 서비스 설정 (대용량 최적화)
jobservice:
  max_job_workers: 100         # 기본 10 → 100
  job_loggers:
    - STD_OUTPUT
    - FILE
    - DB
  logger_sweeper_duration: 1

# 웹훅 설정 (성능 최적화)
notification:
  webhook_job_max_retry: 3     # 기본 10 → 3
  webhook_job_http_client_timeout: 30s

# 차트 박물관 설정
chart:
  absolute_url: disabled

# 로그 설정 (대용량 최적화)
log:
  level: info
  local:
    rotate_count: 200          # 기본 50 → 200
    rotate_size: 1000M         # 기본 200M → 1000M
    location: /var/log/harbor

# Harbor 버전
_version: 2.13.2

# 프록시 설정 (폐쇄망)
proxy:
  http_proxy:
  https_proxy:
  no_proxy: localhost,127.0.0.1,.local,.internal
  components:
    - core
    - jobservice
    - trivy

# 업로드 정리 설정
upload_purging:
  enabled: true
  age: 168h                    # 7일
  interval: 24h
  dryrun: false

# 메트릭 수집 활성화
metric:
  enabled: true
  port: 9090
  path: /metrics

# 캐시 설정
cache:
  enabled: true
  expire_hours: 24
EOF

4. Docker Compose 파일 Podman 최적화

Podman용 docker-compose.yml 수정 준비

# Harbor 설치 전 prepare 실행
./prepare

# Podman 환경에 맞게 docker-compose.yml 수정
cp docker-compose.yml docker-compose.yml.backup

docker-compose.yml 리소스 최적화 (대용량)

# 메모리 및 CPU 제한 추가
cat >> docker-compose.yml.override <<EOF
version: '2.3'
services:
  registry:
    mem_limit: 8g
    memswap_limit: 8g
    cpus: 4.0
    environment:
      - REGISTRY_STORAGE_FILESYSTEM_MAXTHREADS=200
      - REGISTRY_HTTP_TLS_CLIENTCAS_0=/etc/docker/certs.d/ca.crt
    ulimits:
      nofile:
        soft: 65536
        hard: 65536

  postgresql:
    mem_limit: 16g
    memswap_limit: 16g
    cpus: 8.0
    environment:
      - POSTGRES_MAX_CONNECTIONS=2000
      - POSTGRES_SHARED_BUFFERS=4GB
      - POSTGRES_EFFECTIVE_CACHE_SIZE=12GB
      - POSTGRES_WORK_MEM=256MB
      - POSTGRES_MAINTENANCE_WORK_MEM=1GB
      - POSTGRES_CHECKPOINT_COMPLETION_TARGET=0.9
      - POSTGRES_WAL_BUFFERS=64MB
      - POSTGRES_DEFAULT_STATISTICS_TARGET=100
    ulimits:
      nofile:
        soft: 65536
        hard: 65536

  redis:
    mem_limit: 4g
    memswap_limit: 4g
    cpus: 2.0
    command: >
      redis-server
      --maxmemory 3gb
      --maxmemory-policy allkeys-lru
      --save 900 1
      --save 300 10
      --save 60 10000
      --tcp-keepalive 60
      --timeout 0
    ulimits:
      nofile:
        soft: 65536
        hard: 65536

  core:
    mem_limit: 8g
    memswap_limit: 8g
    cpus: 4.0
    ulimits:
      nofile:
        soft: 65536
        hard: 65536

  jobservice:
    mem_limit: 4g
    memswap_limit: 4g
    cpus: 2.0
    ulimits:
      nofile:
        soft: 65536
        hard: 65536

  proxy:
    mem_limit: 2g
    memswap_limit: 2g
    cpus: 2.0
    ulimits:
      nofile:
        soft: 65536
        hard: 65536

  trivy:
    mem_limit: 4g
    memswap_limit: 4g
    cpus: 2.0
    ulimits:
      nofile:
        soft: 65536
        hard: 65536
EOF

5. Nginx 프록시 최적화

nginx.conf 대용량 최적화

# Nginx 설정 백업
cp common/config/nginx/nginx.conf common/config/nginx/nginx.conf.backup

# 대용량 최적화 nginx.conf 생성
cat > common/config/nginx/nginx.conf <<EOF
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;

events {
    use epoll;
    worker_connections 65535;
    multi_accept on;
    accept_mutex off;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    # 로그 형식
    log_format timed_combined '\$remote_addr - '
      '"\$request" \$status \$body_bytes_sent '
      '"\$http_referer" "\$http_user_agent" '
      '\$request_time \$upstream_response_time \$pipe';

    # 로그 설정
    access_log /var/log/nginx/access.log timed_combined buffer=64k flush=5s;
    error_log /var/log/nginx/error.log warn;

    # 기본 설정
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    keepalive_requests 10000;
    types_hash_max_size 2048;
    server_tokens off;

    # 버퍼 크기 최적화
    client_body_buffer_size 1m;
    client_header_buffer_size 16k;
    client_max_body_size 0;
    large_client_header_buffers 8 16k;
    client_body_timeout 300s;
    client_header_timeout 300s;
    send_timeout 300s;

    # 프록시 설정
    proxy_buffering on;
    proxy_buffer_size 8k;
    proxy_buffers 32 8k;
    proxy_busy_buffers_size 16k;
    proxy_temp_file_write_size 16k;
    proxy_connect_timeout 300s;
    proxy_send_timeout 300s;
    proxy_read_timeout 300s;

    # Gzip 압축
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_comp_level 6;
    gzip_types
        application/json
        application/javascript
        application/xml+rss
        application/atom+xml
        image/svg+xml
        text/plain
        text/css
        text/xml
        text/javascript;

    # 업스트림 정의
    upstream core {
        server core:8080 max_fails=3 fail_timeout=30s;
        keepalive 32;
    }

    upstream portal {
        server portal:8080 max_fails=3 fail_timeout=30s;
        keepalive 32;
    }

    # HTTP 서버 (HTTPS로 리다이렉트)
    server {
        listen 80;
        server_name 192.168.1.100;
        
        location / {
            return 301 https://\$server_name\$request_uri;
        }
    }

    # HTTPS 서버
    server {
        listen 443 ssl http2;
        server_name 192.168.1.100;

        # SSL 설정
        ssl_certificate /etc/nginx/cert/harbor.crt;
        ssl_certificate_key /etc/nginx/cert/harbor.key;
        ssl_session_timeout 1d;
        ssl_session_cache shared:SSL:50m;
        ssl_session_tickets off;

        # 현대적인 SSL 설정
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
        ssl_prefer_server_ciphers off;

        # HSTS
        add_header Strict-Transport-Security "max-age=63072000" always;

        # UI 접근
        location / {
            proxy_pass http://portal/;
            proxy_set_header Host \$http_host;
            proxy_set_header X-Real-IP \$remote_addr;
            proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto \$scheme;
            proxy_buffering off;
            proxy_request_buffering off;
        }

        # API 접근
        location /api/ {
            proxy_pass http://core/api/;
            proxy_set_header Host \$http_host;
            proxy_set_header X-Real-IP \$remote_addr;
            proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto \$scheme;
            proxy_buffering off;
            proxy_request_buffering off;
        }

        # Registry API
        location /v2/ {
            proxy_pass http://core/v2/;
            proxy_set_header Host \$http_host;
            proxy_set_header X-Real-IP \$remote_addr;
            proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto \$scheme;
            proxy_buffering off;
            proxy_request_buffering off;
            proxy_send_timeout 900;
            proxy_read_timeout 900;
        }

        # 차트 repository
        location /chartrepo/ {
            proxy_pass http://core/chartrepo/;
            proxy_set_header Host \$http_host;
            proxy_set_header X-Real-IP \$remote_addr;
            proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto \$scheme;
            proxy_buffering off;
            proxy_request_buffering off;
        }

        # 서비스 토큰
        location /service/ {
            proxy_pass http://core/service/;
            proxy_set_header Host \$http_host;
            proxy_set_header X-Real-IP \$remote_addr;
            proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto \$scheme;
            proxy_buffering off;
            proxy_request_buffering off;
        }
    }
}
EOF

6. 스토리지 최적화

대용량 스토리지 준비

# 스토리지 디렉토리 생성 (별도 마운트 포인트 권장)
mkdir -p /harbor-data
mkdir -p /harbor-data/registry
mkdir -p /harbor-data/database
mkdir -p /harbor-data/redis_data
mkdir -p /harbor-data/job_logs

# 권한 설정
chmod -R 755 /harbor-data
chown -R 10000:10000 /harbor-data/registry
chown -R 999:999 /harbor-data/database
chown -R 999:999 /harbor-data/redis_data

# SSD 마운트 예시 (옵션)
# mount /dev/nvme0n1 /harbor-data
# echo '/dev/nvme0n1 /harbor-data ext4 defaults,noatime 0 2' >> /etc/fstab

7. Podman 설정 및 Harbor 설치

Podman 최적화 설정

# Podman 설정 디렉토리 생성
mkdir -p /etc/containers

# registries.conf 설정 (자체 레지스트리 추가)
cat > /etc/containers/registries.conf <<EOF
[registries.search]
registries = ['docker.io', 'quay.io']

[registries.insecure]
registries = []

[registries.block]
registries = []
EOF

# storage.conf 최적화
cat > /etc/containers/storage.conf <<EOF
[storage]
driver = "overlay"
runroot = "/run/containers/storage"
graphroot = "/var/lib/containers/storage"

[storage.options]
additionalimagestores = []
pull_options = {enable_partial_images = "false", use_hard_links = "false", ostree_repos = ""}

[storage.options.overlay]
mountopt = "nodev,metacopy=on"
size = "120G"

[storage.options.thinpool]
autoextend_percent = "20"
autoextend_threshold = "80"
basesize = "100G"
blocksize = "64k"
directlvm_device = ""
directlvm_device_force = "True"
fs = "xfs"
log_level = "7"
min_free_space = "10%"
mkfsarg = ""
mountopt = "nodev"
use_deferred_deletion = "True"
use_deferred_removal = "True"
xfs_nospace_max_retries = "0"
EOF

Podman Compose로 Harbor 설치

# 인증서 생성
chmod +x /opt/harbor/create-harbor-ssl-cert.sh
/opt/harbor/create-harbor-ssl-cert.sh

# Harbor 준비
cd /opt/harbor
./prepare

# Podman Compose로 Harbor 시작
podman-compose -f docker-compose.yml up -d

# 또는 Docker Compose 사용 (Podman 호환 모드)
export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock
docker-compose -f docker-compose.yml up -d

8. 클라이언트 설정

Podman 클라이언트 설정 스크립트

#!/bin/bash
# setup-harbor-podman-client.sh

HARBOR_SERVER="192.168.1.100"
HARBOR_PORT="443"
CA_CERT_FILE="/tmp/harbor-ca.crt"

echo "Harbor Podman 클라이언트 설정..."

# Harbor 서버에서 CA 인증서 복사 필요
if [ ! -f "$CA_CERT_FILE" ]; then
    echo "오류: CA 인증서 파일이 없습니다."
    echo "Harbor 서버에서 다음 명령으로 복사하세요:"
    echo "scp /opt/harbor/certs/ca.crt $(hostname):${CA_CERT_FILE}"
    exit 1
fi

# 시스템 신뢰 저장소에 추가
sudo cp $CA_CERT_FILE /etc/pki/ca-trust/source/anchors/harbor-ca.crt
sudo update-ca-trust

# Podman 인증서 디렉토리 설정
sudo mkdir -p /etc/containers/certs.d/${HARBOR_SERVER}:${HARBOR_PORT}
sudo cp $CA_CERT_FILE /etc/containers/certs.d/${HARBOR_SERVER}:${HARBOR_PORT}/ca.crt

# registries.conf 업데이트
sudo cat >> /etc/containers/registries.conf <<EOF

[[registry]]
location = "${HARBOR_SERVER}:${HARBOR_PORT}"
insecure = false
blocked = false
EOF

echo "✓ Podman 클라이언트 설정 완료"
echo ""
echo "사용 방법:"
echo "podman login ${HARBOR_SERVER}:${HARBOR_PORT}"
echo "podman tag image:tag ${HARBOR_SERVER}:${HARBOR_PORT}/library/image:tag"
echo "podman push ${HARBOR_SERVER}:${HARBOR_PORT}/library/image:tag"

9. 모니터링 및 헬스체크

Harbor 상태 확인 스크립트

#!/bin/bash
# harbor-health-check.sh

HARBOR_SERVER="192.168.1.100"

echo "Harbor 서비스 상태 확인..."

# Podman 컨테이너 상태 확인
echo "=== 컨테이너 상태 ==="
podman ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

echo ""
echo "=== Harbor API 상태 확인 ==="
curl -k -s https://${HARBOR_SERVER}/api/v2.0/systeminfo | jq '.storage'

echo ""
echo "=== 디스크 사용량 확인 ==="
df -h /harbor-data

echo ""
echo "=== 메모리 사용량 확인 ==="
free -h

echo ""
echo "=== 네트워크 포트 확인 ==="
ss -tlnp | grep -E ":80|:443"

echo ""
echo "=== Harbor 로그 확인 (최근 10줄) ==="
podman logs --tail 10 harbor-core

10. 성능 튜닝 및 백업

성능 모니터링 스크립트

#!/bin/bash
# harbor-performance-monitor.sh

HARBOR_SERVER="192.168.1.100"

echo "Harbor 성능 모니터링..."

# 컨테이너별 리소스 사용량
echo "=== 컨테이너 리소스 사용량 ==="
podman stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"

echo ""
echo "=== PostgreSQL 성능 ==="
podman exec harbor-db psql -U postgres -d registry -c "
SELECT 
    datname,
    numbackends,
    xact_commit,
    xact_rollback,
    blks_read,
    blks_hit,
    tup_returned,
    tup_fetched,
    tup_inserted,
    tup_updated,
    tup_deleted
FROM pg_stat_database 
WHERE datname = 'registry';"

echo ""
echo "=== Redis 성능 ==="
podman exec harbor-redis redis-cli info stats

echo ""
echo "=== Registry 저장소 크기 ==="
du -sh /harbor-data/registry/docker/registry/v2/repositories/* | sort -hr | head -20

백업 스크립트

#!/bin/bash
# harbor-backup.sh

BACKUP_DIR="/backup/harbor"
DATE=$(date +%Y%m%d_%H%M%S)
HARBOR_DIR="/opt/harbor"
DATA_DIR="/harbor-data"

echo "Harbor 백업 시작: $DATE"

# 백업 디렉토리 생성
mkdir -p $BACKUP_DIR/$DATE

# Harbor 설정 백업
tar czf $BACKUP_DIR/$DATE/harbor-config-$DATE.tar.gz -C /opt harbor/

# 데이터베이스 백업
podman exec harbor-db pg_dump -U postgres registry > $BACKUP_DIR/$DATE/harbor-db-$DATE.sql

# 인증서 백업
cp -r /opt/harbor/certs $BACKUP_DIR/$DATE/

# 레지스트리 데이터 백업 (선택적 - 용량이 클 수 있음)
# tar czf $BACKUP_DIR/$DATE/harbor-registry-$DATE.tar.gz -C $DATA_DIR registry/

echo "백업 완료: $BACKUP_DIR/$DATE"

설치 완료 후 확인사항

  1. 웹 UI 접속: https://192.168.1.100
  2. 기본 로그인: admin / HarborAdmin2024!
  3. CLI 테스트:
    podman login 192.168.1.100:443
    podman tag hello-world:latest 192.168.1.100:443/library/hello-world:latest
    podman push 192.168.1.100:443/library/hello-world:latest

트러블슈팅

일반적인 문제 해결

# 1. 컨테이너 로그 확인
podman logs harbor-core
podman logs harbor-db
podman logs harbor-redis

# 2. 네트워크 연결 확인
curl -k https://192.168.1.100/api/v2.0/systeminfo

# 3. 인증서 확인
openssl x509 -in /opt/harbor/certs/harbor.crt -text -noout

# 4. 포트 사용 확인
ss -tlnp | grep -E ":80|:443"

# 5. 서비스 재시작
cd /opt/harbor
podman-compose down
podman-compose up -d

11. Kubernetes Containerd Worker 노드 설정

Kubernetes 클러스터에서 Harbor 사용을 위한 설정

containerd 설정 (모든 Worker 노드)

#!/bin/bash
# setup-k8s-containerd-harbor.sh

HARBOR_SERVER="192.168.1.100"
HARBOR_PORT="443"
CA_CERT_FILE="/tmp/harbor-ca.crt"

echo "Kubernetes containerd Harbor 설정 시작..."

# Harbor 서버에서 CA 인증서 복사 필요
if [ ! -f "$CA_CERT_FILE" ]; then
    echo "오류: CA 인증서 파일이 없습니다."
    echo "Harbor 서버에서 다음 명령으로 복사하세요:"
    echo "scp /opt/harbor/certs/ca.crt $(hostname):${CA_CERT_FILE}"
    exit 1
fi

# 1. 시스템 신뢰 저장소에 인증서 추가
sudo cp $CA_CERT_FILE /etc/pki/ca-trust/source/anchors/harbor-ca.crt
sudo update-ca-trust

# 2. containerd 인증서 디렉토리 설정
sudo mkdir -p /etc/containerd/certs.d/${HARBOR_SERVER}:${HARBOR_PORT}
sudo cp $CA_CERT_FILE /etc/containerd/certs.d/${HARBOR_SERVER}:${HARBOR_PORT}/ca.crt

# 3. containerd 설정 백업
sudo cp /etc/containerd/config.toml /etc/containerd/config.toml.backup

# 4. containerd config.toml 수정
sudo cat > /etc/containerd/config.toml <<EOF
version = 2

[plugins]
  [plugins."io.containerd.grpc.v1.cri"]
    sandbox_image = "registry.k8s.io/pause:3.8"
    
    [plugins."io.containerd.grpc.v1.cri".containerd]
      [plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
          runtime_type = "io.containerd.runc.v2"
          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
            SystemdCgroup = true

    [plugins."io.containerd.grpc.v1.cri".registry]
      config_path = "/etc/containerd/certs.d"
      
      [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."${HARBOR_SERVER}:${HARBOR_PORT}"]
          endpoint = ["https://${HARBOR_SERVER}:${HARBOR_PORT}"]
        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."${HARBOR_SERVER}"]
          endpoint = ["https://${HARBOR_SERVER}:${HARBOR_PORT}"]
      
      [plugins."io.containerd.grpc.v1.cri".registry.configs]
        [plugins."io.containerd.grpc.v1.cri".registry.configs."${HARBOR_SERVER}:${HARBOR_PORT}".tls]
          ca_file = "/etc/containerd/certs.d/${HARBOR_SERVER}:${HARBOR_PORT}/ca.crt"
          insecure_skip_verify = false
        [plugins."io.containerd.grpc.v1.cri".registry.configs."${HARBOR_SERVER}:${HARBOR_PORT}".auth]
          username = "admin"
          password = "HarborAdmin2024!"

  [plugins."io.containerd.internal.v1.opt"]
    path = "/opt/containerd"
  
  [plugins."io.containerd.internal.v1.restart"]
    interval = "10s"
  
  [plugins."io.containerd.metadata.v1.bolt"]
    content_sharing_policy = "shared"

[timeouts]
  "io.containerd.timeout.shim.cleanup" = "5s"
  "io.containerd.timeout.shim.load" = "5s"
  "io.containerd.timeout.shim.shutdown" = "3s"
  "io.containerd.timeout.task.state" = "2s"

[imports]
  path = "/etc/containerd/conf.d/*.toml"
EOF

# 5. containerd 재시작
sudo systemctl restart containerd
sudo systemctl enable containerd

echo "✓ containerd 설정 완료"
echo ""
echo "테스트 명령:"
echo "sudo crictl pull ${HARBOR_SERVER}:${HARBOR_PORT}/library/hello-world:latest"

Registry 인증서 개별 설정 (권장 방법)

# Harbor 서버별 상세 설정
sudo mkdir -p /etc/containerd/certs.d/192.168.1.100:443

# hosts.toml 파일 생성 (더 세밀한 제어)
sudo cat > /etc/containerd/certs.d/192.168.1.100:443/hosts.toml <<EOF
server = "https://192.168.1.100:443"

[host."https://192.168.1.100:443"]
  capabilities = ["pull", "resolve", "push"]
  ca = "/etc/containerd/certs.d/192.168.1.100:443/ca.crt"
  skip_verify = false
  
  # 인증 설정 (옵션 - 클러스터에서는 imagePullSecrets 사용 권장)
  # [host."https://192.168.1.100:443".header]
  #   authorization = "Basic YWRtaW46SGFyYm9yQWRtaW4yMDI0IQ=="
EOF

Kubernetes Secret 및 ServiceAccount 설정

Docker Registry Secret 생성

#!/bin/bash
# create-k8s-harbor-secret.sh

HARBOR_SERVER="192.168.1.100:443"
HARBOR_USERNAME="admin"
HARBOR_PASSWORD="HarborAdmin2024!"
NAMESPACE="default"

echo "Kubernetes Harbor Secret 생성..."

# 1. Docker Registry Secret 생성
kubectl create secret docker-registry harbor-registry-secret \
  --docker-server=${HARBOR_SERVER} \
  --docker-username=${HARBOR_USERNAME} \
  --docker-password=${HARBOR_PASSWORD} \
  --docker-email=admin@harbor.local \
  --namespace=${NAMESPACE}

# 2. 여러 네임스페이스에 Secret 복사
for ns in kube-system harbor-system monitoring; do
  kubectl create namespace $ns --dry-run=client -o yaml | kubectl apply -f -
  kubectl create secret docker-registry harbor-registry-secret \
    --docker-server=${HARBOR_SERVER} \
    --docker-username=${HARBOR_USERNAME} \
    --docker-password=${HARBOR_PASSWORD} \
    --docker-email=admin@harbor.local \
    --namespace=$ns
done

# 3. Default ServiceAccount에 imagePullSecrets 추가
kubectl patch serviceaccount default \
  -p '{"imagePullSecrets": [{"name": "harbor-registry-secret"}]}' \
  --namespace=${NAMESPACE}

echo "✓ Kubernetes Secret 설정 완료"

YAML 파일을 이용한 Secret 생성

# harbor-registry-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: harbor-registry-secret
  namespace: default
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: ewogICJhdXRocyI6IHsKICAgICIxOTIuMTY4LjEuMTAwOjQ0MyI6IHsKICAgICAgInVzZXJuYW1lIjogImFkbWluIiwKICAgICAgInBhc3N3b3JkIjogIkhhcmJvckFkbWluMjAyNCEiLAogICAgICAiYXV0aCI6ICJZV1J0YVc0NlNHRnlZbTl5UVdSdGFXNHlNREkwSVE9PSIKICAgIH0KICB9Cn0=

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: harbor-service-account
  namespace: default
imagePullSecrets:
- name: harbor-registry-secret

Pod에서 Harbor 이미지 사용 예시

기본 Pod 배포

# test-harbor-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-harbor-pod
  namespace: default
spec:
  imagePullSecrets:
  - name: harbor-registry-secret
  containers:
  - name: test-container
    image: 192.168.1.100:443/library/nginx:latest
    ports:
    - containerPort: 80
  restartPolicy: Always

Deployment 배포

# harbor-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: harbor-nginx-deployment
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: harbor-nginx
  template:
    metadata:
      labels:
        app: harbor-nginx
    spec:
      imagePullSecrets:
      - name: harbor-registry-secret
      containers:
      - name: nginx
        image: 192.168.1.100:443/library/nginx:latest
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"

자동화된 전체 설정 스크립트

#!/bin/bash
# k8s-harbor-full-setup.sh

HARBOR_SERVER="192.168.1.100"
HARBOR_PORT="443"
HARBOR_USERNAME="admin"
HARBOR_PASSWORD="HarborAdmin2024!"
CA_CERT_PATH="/tmp/harbor-ca.crt"

# 색상 정의
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

print_status() {
    echo -e "${GREEN}[INFO]${NC} $1"
}

print_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

print_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# 1. 전제조건 확인
print_status "전제조건 확인 중..."

if [ ! -f "$CA_CERT_PATH" ]; then
    print_error "CA 인증서 파일이 없습니다: $CA_CERT_PATH"
    echo "Harbor 서버에서 다음 명령으로 복사하세요:"
    echo "scp /opt/harbor/certs/ca.crt $(hostname):${CA_CERT_PATH}"
    exit 1
fi

if ! command -v kubectl &> /dev/null; then
    print_error "kubectl이 설치되어 있지 않습니다."
    exit 1
fi

if ! command -v crictl &> /dev/null; then
    print_error "crictl이 설치되어 있지 않습니다."
    exit 1
fi

# 2. 시스템 인증서 설정
print_status "시스템 인증서 설정 중..."
sudo cp $CA_CERT_PATH /etc/pki/ca-trust/source/anchors/harbor-ca.crt
sudo update-ca-trust

# 3. containerd 설정
print_status "containerd 설정 중..."
sudo mkdir -p /etc/containerd/certs.d/${HARBOR_SERVER}:${HARBOR_PORT}
sudo cp $CA_CERT_PATH /etc/containerd/certs.d/${HARBOR_SERVER}:${HARBOR_PORT}/ca.crt

# containerd hosts.toml 설정
sudo cat > /etc/containerd/certs.d/${HARBOR_SERVER}:${HARBOR_PORT}/hosts.toml <<EOF
server = "https://${HARBOR_SERVER}:${HARBOR_PORT}"

[host."https://${HARBOR_SERVER}:${HARBOR_PORT}"]
  capabilities = ["pull", "resolve", "push"]
  ca = "/etc/containerd/certs.d/${HARBOR_SERVER}:${HARBOR_PORT}/ca.crt"
  skip_verify = false
EOF

# containerd 설정 업데이트
if ! grep -q "config_path.*certs.d" /etc/containerd/config.toml; then
    print_status "containerd config.toml 업데이트 중..."
    sudo cp /etc/containerd/config.toml /etc/containerd/config.toml.backup
    
    # registry 설정이 없으면 추가
    if ! grep -q "\[plugins.\"io.containerd.grpc.v1.cri\".registry\]" /etc/containerd/config.toml; then
        sudo tee -a /etc/containerd/config.toml > /dev/null <<EOF

    [plugins."io.containerd.grpc.v1.cri".registry]
      config_path = "/etc/containerd/certs.d"
EOF
    fi
fi

# containerd 재시작
print_status "containerd 재시작 중..."
sudo systemctl restart containerd

# 4. Kubernetes Secret 생성
print_status "Kubernetes Secret 생성 중..."

# Base64 인코딩된 docker config 생성
DOCKER_CONFIG=$(echo -n "${HARBOR_USERNAME}:${HARBOR_PASSWORD}" | base64)
DOCKER_CONFIG_JSON=$(cat <<EOF | base64 -w 0
{
  "auths": {
    "${HARBOR_SERVER}:${HARBOR_PORT}": {
      "username": "${HARBOR_USERNAME}",
      "password": "${HARBOR_PASSWORD}",
      "auth": "${DOCKER_CONFIG}"
    }
  }
}
EOF
)

# Secret YAML 생성 및 적용
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: harbor-registry-secret
  namespace: default
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: ${DOCKER_CONFIG_JSON}
EOF

# 5. 여러 네임스페이스에 Secret 복사
NAMESPACES=("kube-system" "harbor-system" "monitoring" "ingress-nginx")
for ns in "${NAMESPACES[@]}"; do
    print_status "네임스페이스 '$ns'에 Secret 생성 중..."
    kubectl create namespace $ns --dry-run=client -o yaml | kubectl apply -f -
    kubectl create secret docker-registry harbor-registry-secret \
      --docker-server=${HARBOR_SERVER}:${HARBOR_PORT} \
      --docker-username=${HARBOR_USERNAME} \
      --docker-password=${HARBOR_PASSWORD} \
      --docker-email=admin@harbor.local \
      --namespace=$ns \
      --dry-run=client -o yaml | kubectl apply -f -
done

# 6. Default ServiceAccount 업데이트
print_status "Default ServiceAccount에 imagePullSecrets 추가 중..."
kubectl patch serviceaccount default \
  -p '{"imagePullSecrets": [{"name": "harbor-registry-secret"}]}' \
  --namespace=default

# 7. 연결 테스트
print_status "Harbor 연결 테스트 중..."

# crictl을 이용한 이미지 pull 테스트
if sudo crictl pull ${HARBOR_SERVER}:${HARBOR_PORT}/library/hello-world:latest 2>/dev/null; then
    print_status "✓ crictl을 통한 Harbor 연결 성공"
else
    print_warning "crictl을 통한 Harbor 연결 실패 (이미지가 없을 수 있음)"
fi

# kubectl을 이용한 테스트 Pod 생성
print_status "테스트 Pod 생성 중..."
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: harbor-test-pod
  namespace: default
spec:
  imagePullSecrets:
  - name: harbor-registry-secret
  containers:
  - name: test
    image: ${HARBOR_SERVER}:${HARBOR_PORT}/library/nginx:latest
    command: ["sleep", "3600"]
  restartPolicy: Never
EOF

# 8. 결과 확인
print_status "설정 완료!"
echo ""
echo "=== 설정 요약 ==="
echo "Harbor 서버: ${HARBOR_SERVER}:${HARBOR_PORT}"
echo "인증서 위치: /etc/containerd/certs.d/${HARBOR_SERVER}:${HARBOR_PORT}/ca.crt"
echo "Secret 이름: harbor-registry-secret"
echo ""
echo "=== 테스트 명령 ==="
echo "1. crictl 테스트:"
echo "   sudo crictl pull ${HARBOR_SERVER}:${HARBOR_PORT}/library/nginx:latest"
echo ""
echo "2. kubectl 테스트:"
echo "   kubectl get pod harbor-test-pod"
echo "   kubectl describe pod harbor-test-pod"
echo ""
echo "3. Pod에서 Harbor 이미지 사용:"
echo "   image: ${HARBOR_SERVER}:${HARBOR_PORT}/library/your-image:tag"
echo "   imagePullSecrets:"
echo "   - name: harbor-registry-secret"

Harbor 이미지 Push 및 사용 워크플로우

# 1. 로컬에서 이미지 빌드 및 Harbor에 푸시
docker build -t myapp:v1.0 .
docker tag myapp:v1.0 192.168.1.100:443/library/myapp:v1.0
docker push 192.168.1.100:443/library/myapp:v1.0

# 2. Kubernetes에서 배포
kubectl create deployment myapp \
  --image=192.168.1.100:443/library/myapp:v1.0 \
  --dry-run=client -o yaml > myapp-deployment.yaml

# imagePullSecrets 추가 후 배포
kubectl apply -f myapp-deployment.yaml

트러블슈팅 가이드

# 1. containerd 설정 확인
sudo crictl config get

# 2. 인증서 확인
openssl x509 -in /etc/containerd/certs.d/192.168.1.100:443/ca.crt -text -noout

# 3. containerd 로그 확인
sudo journalctl -u containerd -f

# 4. 이미지 pull 상세 로그
sudo crictl -D pull 192.168.1.100:443/library/nginx:latest

# 5. kubelet 로그 확인
sudo journalctl -u kubelet -f

# 6. Pod imagePull 실패 시 확인
kubectl describe pod [pod-name]
kubectl get events --sort-by=.metadata.creationTimestamp
profile
bytebliss

0개의 댓글