실무 가이드: Containerd, Harbor, Docker-Compose를 활용한 컨테이너 인프라 구축

1. Containerd 및 CNI 플러그인 설치 (바이너리 방식)

컨테이너 런타임 환경을 구축하기 위해 containerd와 CNI(Container Network Interface) 플러그인을 바이너리 파일로 직접 설치합니다.

1.1 containerd 바이너리 및 서비스 파일 준비

# containerd 바이너리 다운로드
root@ubuntu-node:~# wget https://github.com/containerd/containerd/releases/download/v1.7.2/containerd-1.7.2-linux-amd64.tar.gz

# 압축 해제 및 바이너리 복사
root@ubuntu-node:~# tar xvf containerd-1.7.2-linux-amd64.tar.gz -C /usr/local/
root@ubuntu-node:~# cp /usr/local/bin/* /usr/local/bin/

# 설치 확인
root@ubuntu-node:~# containerd -v
containerd github.com/containerd/containerd v1.7.2

# systemd 서비스 파일 생성
root@ubuntu-node:~# cat <<EOF | sudo tee /etc/systemd/system/containerd.service
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target local-fs.target

[Service]
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/containerd
Type=notify
Delegate=yes
KillMode=process
Restart=always
RestartSec=5
LimitNPROC=infinity
LimitCORE=infinity
LimitNOFILE=infinity
TasksMax=infinity
OOMScoreAdjust=-999

[Install]
WantedBy=multi-user.target
EOF

1.2 containerd 환경 설정

# 기본 설정 파일 생성
root@ubuntu-node:~# mkdir -p /etc/containerd
root@ubuntu-node:~# containerd config default > /etc/containerd/config.toml

# 설정 파일 수정 (sandbox 이미지 및 미러 레포지토리)
root@ubuntu-node:~# sed -i 's|sandbox_image = ".*"|sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"|' /etc/containerd/config.toml
root@ubuntu-node:~# sed -i '/\[plugins\."io.containerd.grpc.v1.cri".registry.mirrors\]/a\ \       [plugins\."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]\n\ \         endpoint = ["https://frncu3gx.mirror.aliyuncs.com"]' /etc/containerd/config.toml

# 서비스 시작 및 활성화
root@ubuntu-node:~# systemctl daemon-reload
root@ubuntu-node:~# systemctl start containerd
root@ubuntu-node:~# systemctl enable containerd
root@ubuntu-node:~# systemctl status containerd

1.3 runc 설치

root@ubuntu-node:~# wget -O runc https://github.com/opencontainers/runc/releases/download/v1.1.8/runc.amd64
root@ubuntu-node:~# chmod +x runc
root@ubuntu-node:~# mv runc /usr/local/bin/runc
root@ubuntu-node:~# runc -v
runc version 1.1.8

1.4 CNI 플러그인 설치

root@ubuntu-node:~# wget https://github.com/containernetworking/plugins/releases/download/v1.3.0/cni-plugins-linux-amd64-v1.3.0.tgz
root@ubuntu-node:~# mkdir -p /opt/cni/bin
root@ubuntu-node:~# tar xvf cni-plugins-linux-amd64-v1.3.0.tgz -C /opt/cni/bin/

2. nerdctl을 이용한 컨테이너 관리

2.1 nerdctl 설치 및 설정

root@ubuntu-node:~# wget https://github.com/containerd/nerdctl/releases/download/v1.4.0/nerdctl-1.4.0-linux-amd64.tar.gz
root@ubuntu-node:~# tar xvf nerdctl-1.4.0-linux-amd64.tar.gz -C /usr/local/bin/

# nerdctl 설정 파일 생성
root@ubuntu-node:~# mkdir -p /etc/nerdctl
root@ubuntu-node:~# cat <<EOF | sudo tee /etc/nerdctl/nerdctl.toml
namespace = "k8s.io"
debug = false
debug_full = false
insecure_registry = true
EOF

2.2 nerdctl 기본 명령어

# Nginx 컨테이너 실행
root@ubuntu-node:~# nerdctl run -d --name web-nginx -p 8080:80 nginx:latest

# 실행 중인 컨테이너 확인
root@ubuntu-node:~# nerdctl ps
CONTAINER ID    IMAGE                   COMMAND                  CREATED         STATUS    PORTS                   NAMES
c8f1e37a23b4    docker.io/library/nginx:latest   "/docker-entrypoint.…"  15 seconds ago  Up        0.0.0.0:8080->80/tcp    web-nginx

# 컨테이너 내부 접속
root@ubuntu-node:~# nerdctl exec -it c8f1e37a23b4 bash
root@c8f1e37a23b4:/# hostname -I
10.4.0.2
root@c8f1e37a23b4:/# curl -I www.baidu.com
HTTP/1.1 200 OK

# 컨테이너 중지 및 삭제
root@ubuntu-node:~# nerdctl stop web-nginx
root@ubuntu-node:~# nerdctl rm web-nginx

# 이미지 목록 확인
root@ubuntu-node:~# nerdctl images
REPOSITORY    TAG       IMAGE ID        CREATED         PLATFORM       SIZE         BLOB SIZE
nginx         latest    08bc36ad5247    2 hours ago     linux/amd64    192.1 MiB    67.3 MiB

3. HTTPS 기반 Harbor 레지스트리 구축

3.1 사전 준비: 인증서 파일

미리 발급받은 SSL 인증서를 서버에 업로드합니다.

root@harbor-node:~# mkdir -p /data/certfile /data/privatekey
root@harbor-node:~# ls -l /data/certfile/ /data/privatekey/
/data/certfile:
total 4
-rw-r--r-- 1 root root 1675 Jul 25 10:00 yourdomain.pem

/data/privatekey:
total 4
-rw-r--r-- 1 root root 1675 Jul 25 10:00 yourdomain.key

3.2 Harbor 설치

root@harbor-node:~# wget https://github.com/goharbor/harbor/releases/download/v2.8.2/harbor-offline-installer-v2.8.2.tgz
root@harbor-node:~# tar xvf harbor-offline-installer-v2.8.2.tgz -C /opt/
root@harbor-node:~# cd /opt/harbor
root@harbor-node:/opt/harbor# cp harbor.yml.tmpl harbor.yml

# 설정 파일 수정
root@harbor-node:/opt/harbor# vim harbor.yml
# hostname: yourdomain.com
# https:
#   port: 443
#   certificate: /data/certfile/yourdomain.pem
#   private_key: /data/privatekey/yourdomain.key
# harbor_admin_password: YourSecurePassword
# data_volume: /data/harbor

# 설치 실행 (보안 스캐너 Trivy 포함)
root@harbor-node:/opt/harbor# ./install.sh --with-trivy

# 설치 로그 마지막 부분
✔ ----Harbor has been installed and started successfully.----

3.3 DNS 설정

클라이언트 호스트 파일에 도메인을 추가합니다.

root@client-node:~# echo "192.168.1.100 yourdomain.com" >> /etc/hosts

3.4 nerdctl을 이용한 이미지 Push/Pull

# Harbor 로그인
root@client-node:~# nerdctl login yourdomain.com
Username: admin
Password:
Login Succeeded

# 이미지 태그 및 Push
root@client-node:~# nerdctl tag nginx:latest yourdomain.com/library/nginx:latest
root@client-node:~# nerdctl push yourdomain.com/library/nginx:latest

# 이미지 Pull
root@client-node:~# nerdctl pull yourdomain.com/library/nginx:latest

4. 멀티 스테이지를 활용한 Nginx 이미지 빌드

4.1 Ubuntu 기반 Nginx 이미지 Dockerfile

root@build-node:~/ubuntu-nginx# cat <<'EOF' > Dockerfile
# 1단계: 빌드 스테이지
FROM ubuntu:22.04 AS builder

RUN apt update && apt install -y \
    wget \
    gcc \
    libpcre3-dev \
    zlib1g-dev \
    make

# Nginx 소스 다운로드 및 컴파일
RUN wget http://nginx.org/download/nginx-1.18.0.tar.gz && \
    tar xzf nginx-1.18.0.tar.gz && \
    cd nginx-1.18.0 && \
    ./configure --prefix=/opt/nginx --user=nginx --group=nginx && \
    make && make install

# 2단계: 실행 스테이지
FROM ubuntu:22.04 AS runtime

# 런타임 의존성 설치
RUN apt update && apt install -y \
    iproute2 \
    tcpdump \
    curl \
    vim \
    iputils-ping

# 빌드 스테이지에서 nginx 실행 파일 복사
COPY --from=builder /opt/nginx /opt/nginx

# 사용자 생성 및 설정
RUN useradd -r -s /sbin/nologin nginx && \
    chown -R nginx:nginx /opt/nginx

# 설정 및 정적 파일 추가
COPY nginx.conf /opt/nginx/conf/
COPY index.html /opt/nginx/html/

EXPOSE 80
CMD ["/opt/nginx/sbin/nginx", "-g", "daemon off;"]
EOF

# 이미지 빌드
root@build-node:~/ubuntu-nginx# docker build -t yourdomain.com/library/nginx:ubuntu-v1 .

4.2 Alpine 기반 Nginx 이미지 Dockerfile

root@build-node:~/alpine-nginx# cat <<'EOF' > Dockerfile
FROM alpine:3.18 AS builder

RUN apk add --no-cache \
    build-base \
    pcre-dev \
    zlib-dev \
    openssl-dev

RUN wget http://nginx.org/download/nginx-1.18.0.tar.gz && \
    tar xzf nginx-1.18.0.tar.gz && \
    cd nginx-1.18.0 && \
    ./configure --prefix=/opt/nginx --user=nginx --group=nginx && \
    make && make install

FROM alpine:3.18 AS runtime

RUN addgroup -S nginx && adduser -S -G nginx nginx

COPY --from=builder /opt/nginx /opt/nginx
COPY nginx.conf /opt/nginx/conf/
COPY index.html /opt/nginx/html/

EXPOSE 80
CMD ["/opt/nginx/sbin/nginx", "-g", "daemon off;"]
EOF

# 이미지 빌드
root@build-node:~/alpine-nginx# docker build -t yourdomain.com/library/nginx:alpine-v1 .

5. Docker Compose를 활용한 다중 컨테이너 애플리케이션

5.1 Docker Compose 설치

root@app-node:~# curl -L "https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
root@app-node:~# chmod +x /usr/local/bin/docker-compose
root@app-node:~# docker-compose --version
Docker Compose version v2.23.0

5.2 docker-compose.yml 작성

root@app-node:~/myapp# cat <<'EOF' > docker-compose.yml
version: '3.8'

services:
  nginx-proxy:
    image: nginx:1.25-alpine
    container_name: web-proxy
    ports:
      - "80:80"
      - "443:443"
    networks:
      - frontend
      - backend
    depends_on:
      - tomcat-app
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro

  tomcat-app:
    image: yourdomain.com/library/tomcat-myapp:v1
    container_name: app-server
    networks:
      - backend
    depends_on:
      - mysql-db

  mysql-db:
    image: mysql:8.0
    container_name: database
    environment:
      MYSQL_ROOT_PASSWORD: MySecretPass
      TZ: Asia/Seoul
    volumes:
      - mysql_data:/var/lib/mysql
    networks:
      - backend

volumes:
  mysql_data:

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
EOF

# Nginx 설정 파일
root@app-node:~/myapp# mkdir nginx
root@app-node:~/myapp# cat <<'EOF' > nginx/default.conf
server {
    listen 80;
    server_name localhost;

    location /myapp {
        proxy_pass http://tomcat-app:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}
EOF

5.3 애플리케이션 실행

root@app-node:~/myapp# docker-compose up -d

# 실행 상태 확인
root@app-node:~/myapp# docker-compose ps
NAME                IMAGE               COMMAND                  SERVICE             STATUS              PORTS
web-proxy           nginx:1.25-alpine   "/docker-entrypoint.…"   nginx-proxy         running             0.0.0.0:80->80/tcp
app-server          tomcat-myapp:v1     "/apps/tomcat/bin/do…"   tomcat-app          running             8080/tcp
database            mysql:8.0           "docker-entrypoint.s…"   mysql-db            running             3306/tcp

태그: containerd CNI nerdctl Harbor Dockerfile

5월 25일 15:22에 게시됨