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
sudo dnf install -y podman-compose
sudo dnf install -y python3-pip
pip3 install docker-compose
1. 시스템 최적화 설정
커널 파라미터 튜닝
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
시스템 제한 설정
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
SERVER_IP="192.168.1.100"
CERT_DIR="/opt/harbor/certs"
echo "Harbor 대용량 환경용 SSL 인증서 생성..."
mkdir -p ${CERT_DIR}
cd ${CERT_DIR}
openssl genrsa -out ca.key 4096
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"
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
openssl req -new -key harbor.key -out harbor.csr -config harbor.conf
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
tar xvf harbor-offline-installer-v2.13.2.tgz
mv harbor /opt/
cd /opt/harbor
대용량 클러스터용 harbor.yml 설정
cp harbor.yml.tmpl 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 수정 준비
./prepare
cp docker-compose.yml docker-compose.yml.backup
docker-compose.yml 리소스 최적화 (대용량)
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 대용량 최적화
cp common/config/nginx/nginx.conf common/config/nginx/nginx.conf.backup
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
7. Podman 설정 및 Harbor 설치
Podman 최적화 설정
mkdir -p /etc/containers
cat > /etc/containers/registries.conf <<EOF
[registries.search]
registries = ['docker.io', 'quay.io']
[registries.insecure]
registries = []
[registries.block]
registries = []
EOF
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
cd /opt/harbor
./prepare
podman-compose -f docker-compose.yml up -d
export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock
docker-compose -f docker-compose.yml up -d
8. 클라이언트 설정
Podman 클라이언트 설정 스크립트
#!/bin/bash
HARBOR_SERVER="192.168.1.100"
HARBOR_PORT="443"
CA_CERT_FILE="/tmp/harbor-ca.crt"
echo "Harbor Podman 클라이언트 설정..."
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
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
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_SERVER="192.168.1.100"
echo "Harbor 서비스 상태 확인..."
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_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
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
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/
echo "백업 완료: $BACKUP_DIR/$DATE"
설치 완료 후 확인사항
- 웹 UI 접속: https://192.168.1.100
- 기본 로그인: admin / HarborAdmin2024!
- 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
트러블슈팅
일반적인 문제 해결
podman logs harbor-core
podman logs harbor-db
podman logs harbor-redis
curl -k https://192.168.1.100/api/v2.0/systeminfo
openssl x509 -in /opt/harbor/certs/harbor.crt -text -noout
ss -tlnp | grep -E ":80|:443"
cd /opt/harbor
podman-compose down
podman-compose up -d
11. Kubernetes Containerd Worker 노드 설정
Kubernetes 클러스터에서 Harbor 사용을 위한 설정
containerd 설정 (모든 Worker 노드)
#!/bin/bash
HARBOR_SERVER="192.168.1.100"
HARBOR_PORT="443"
CA_CERT_FILE="/tmp/harbor-ca.crt"
echo "Kubernetes containerd Harbor 설정 시작..."
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
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
sudo cp /etc/containerd/config.toml /etc/containerd/config.toml.backup
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
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 인증서 개별 설정 (권장 방법)
sudo mkdir -p /etc/containerd/certs.d/192.168.1.100:443
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
HARBOR_SERVER="192.168.1.100:443"
HARBOR_USERNAME="admin"
HARBOR_PASSWORD="HarborAdmin2024!"
NAMESPACE="default"
echo "Kubernetes Harbor 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}
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
kubectl patch serviceaccount default \
-p '{"imagePullSecrets": [{"name": "harbor-registry-secret"}]}' \
--namespace=${NAMESPACE}
echo "✓ Kubernetes Secret 설정 완료"
YAML 파일을 이용한 Secret 생성
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 배포
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 배포
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
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"
}
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
print_status "시스템 인증서 설정 중..."
sudo cp $CA_CERT_PATH /etc/pki/ca-trust/source/anchors/harbor-ca.crt
sudo update-ca-trust
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
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
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
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
print_status "containerd 재시작 중..."
sudo systemctl restart containerd
print_status "Kubernetes Secret 생성 중..."
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
)
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
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
print_status "Default ServiceAccount에 imagePullSecrets 추가 중..."
kubectl patch serviceaccount default \
-p '{"imagePullSecrets": [{"name": "harbor-registry-secret"}]}' \
--namespace=default
print_status "Harbor 연결 테스트 중..."
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
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
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 및 사용 워크플로우
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
kubectl create deployment myapp \
--image=192.168.1.100:443/library/myapp:v1.0 \
--dry-run=client -o yaml > myapp-deployment.yaml
kubectl apply -f myapp-deployment.yaml
트러블슈팅 가이드
sudo crictl config get
openssl x509 -in /etc/containerd/certs.d/192.168.1.100:443/ca.crt -text -noout
sudo journalctl -u containerd -f
sudo crictl -D pull 192.168.1.100:443/library/nginx:latest
sudo journalctl -u kubelet -f
kubectl describe pod [pod-name]
kubectl get events --sort-by=.metadata.creationTimestamp