소개
이 랩에서는 Docker 를 사용하여 Red Hat Enterprise Linux (RHEL) 9 컨테이너를 배포하고 자동화하는 기본 사항을 배우게 됩니다. 현대 클라우드 네이티브 배포는 기존 가상 머신보다 컨테이너화된 RHEL 환경에 점점 더 의존하고 있습니다. 먼저, 컨테이너 형식으로 엔터프라이즈급 RHEL 환경을 제공하는 Red Hat 의 Universal Base Images (UBI) 를 살펴보겠습니다.
전통적인 Kickstart 개념이 컨테이너 자동화로 어떻게 변환되는지 살펴보고, 설치 구성을 미러링하는 사용자 지정 Dockerfile 을 생성하며, 자동화된 배포 스크립트를 구축합니다. 이 랩을 마치면 RHEL 컨테이너를 효율적으로 배포하고 현대 클라우드 환경에서 일관되고 반복 가능한 배포를 위해 프로세스를 자동화하는 방법을 이해하게 될 것입니다.
Red Hat Universal Base Images (UBI) 탐색
이 단계에서는 RHEL 을 기반으로 하는 엔터프라이즈급 컨테이너 이미지인 Red Hat 의 Universal Base Images (UBI) 를 탐색합니다. 전체 가상 머신이 필요한 기존 RHEL 설치와 달리, UBI 이미지는 가볍고 이식 가능한 컨테이너에서 RHEL 환경을 제공합니다. 이러한 이미지는 자유롭게 재배포할 수 있으며 현대 클라우드 네이티브 애플리케이션을 위해 설계되었습니다.
Red Hat 은 다양한 사용 사례에 최적화된 여러 UBI 변형을 제공합니다. redhat/ubi9 이미지는 dnf 패키지 관리자를 포함하는 전체 RHEL 기반 컨테이너 환경을 제공하여 소프트웨어 설치 및 시스템 자동화가 필요한 애플리케이션에 적합합니다.
먼저, 이 랩을 위해 준비된 Red Hat 컨테이너 구성 템플릿을 살펴보겠습니다. 이 파일은 기존 Kickstart 개념이 컨테이너 환경으로 어떻게 변환되는지 보여줍니다.
sudo cat /etc/labex/rhel-container-config.cfg
기존 설치 개념을 미러링하는 Dockerfile 스타일의 구성을 보여주는 출력을 보게 됩니다.
## RHEL Container Configuration Template
## Based on traditional Kickstart concepts adapted for containers
## Base image specification
FROM redhat/ubi9
## System locale and timezone
ENV LANG=en_US.UTF-8
ENV TZ=America/New_York
## User configuration
ENV CONTAINER_USER=labex
ENV ROOT_PASSWORD=redhat
## Package installation
## Packages: httpd, curl (container-appropriate equivalents)
RUN dnf install -y --allowerasing httpd curl && \
dnf clean all
## Service configuration
EXPOSE 80
## Startup command
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]
이제 사용 가능한 Red Hat UBI 이미지를 살펴보겠습니다. 먼저 Docker 가 실행 중이고 접근 가능한지 확인합니다.
docker --version
전체 RHEL 기반 컨테이너 환경을 제공하는 Red Hat UBI 9 이미지를 가져옵니다.
docker pull redhat/ubi9
다음과 유사한 출력을 볼 수 있습니다.
Using default tag: latest
latest: Pulling from redhat/ubi9
Digest: sha256:...
Status: Downloaded newer image for redhat/ubi9:latest
docker.io/redhat/ubi9:latest
가져오기가 성공했는지 확인하기 위해 다운로드된 이미지를 나열합니다.
docker images redhat/ubi9
출력은 이미지에 대한 세부 정보를 표시합니다.
REPOSITORY TAG IMAGE ID CREATED SIZE
redhat/ubi9 latest b1c2d3e4f5g6 5 days ago 216MB
이제 RHEL 환경을 탐색하기 위해 기본 컨테이너를 실행해 보겠습니다.
docker run -it --rm redhat/ubi9 /bin/bash
컨테이너 내부에서 운영 체제 버전을 확인하여 RHEL 환경을 탐색합니다.
cat /etc/redhat-release
다음과 유사한 내용을 볼 수 있습니다.
Red Hat Enterprise Linux release 9.6 (Plow)
사용 가능한 패키지 관리자를 확인합니다.
dnf --version
다음을 입력하여 컨테이너를 종료합니다.
exit
사용자 정의를 위해 템플릿 구성을 프로젝트 디렉토리로 복사합니다.
sudo cp /etc/labex/rhel-container-config.cfg ~/project/rhel-container.dockerfile
sudo chown labex:labex ~/project/rhel-container.dockerfile
파일이 성공적으로 복사되었는지 확인합니다.
ls -l ~/project/rhel-container.dockerfile
이제 Red Hat UBI 이미지를 성공적으로 탐색했으며 다음 단계에서 사용자 지정 컨테이너 구성을 만들 준비가 되었습니다.
맞춤형 RHEL 컨테이너 구성 생성
이 단계에서는 Red Hat UBI 이미지를 기반으로 사용자 지정 Dockerfile 을 생성합니다. 이 프로세스는 자동 설치를 위해 Kickstart 파일을 사용자 지정하는 방법과 유사하지만 컨테이너 환경에 맞게 조정되었습니다. Dockerfile 은 일관된 RHEL 컨테이너 배포를 생성하기 위한 자동화 템플릿 역할을 합니다.
먼저, 프로젝트 디렉토리에 있는지 확인합니다.
cd ~/project
자동화된 RHEL 컨테이너 배포를 위해 새롭고 더 구체적인 Dockerfile 을 생성합니다.
cp rhel-container.dockerfile rhel9-automated.dockerfile
두 파일이 존재하는지 확인합니다.
ls -l *.dockerfile
두 파일을 모두 볼 수 있습니다.
-rw-r--r--. 1 labex labex 423 Jul 22 10:30 rhel-container.dockerfile
-rw-r--r--. 1 labex labex 423 Jul 22 10:35 rhel9-automated.dockerfile
이제 사용자 지정을 시작하기 전에 새 Dockerfile 을 열어 구조를 검토합니다.
nano rhel9-automated.dockerfile
파일 내부에서 Kickstart 지시어에 해당하는 컨테이너를 볼 수 있습니다.
- FROM 지시어: 기본 RHEL 이미지 (설치 미디어에 해당) 를 지정합니다.
- ENV 지시어: 환경 변수를 설정합니다 (시스템 구성에 해당).
- RUN 지시어: 이미지 빌드 중에 명령을 실행합니다 (패키지 설치에 해당).
- EXPOSE 및 CMD: 서비스 및 시작을 구성합니다 (서비스 구성에 해당).
지금은 Ctrl+X를 눌러 편집기를 종료하고 사용자 지정 단계로 진행합니다.
이 구조를 이해하면 자동화된 VM 설치를 위해 Kickstart 파일을 사용자 지정하는 것처럼 특정 배포 요구 사항을 충족하도록 컨테이너 구성을 사용자 지정하는 다음 단계를 준비할 수 있습니다.
자동 배포를 위한 컨테이너 구성 사용자 정의
이 단계에서는 rhel9-automated.dockerfile의 내용을 자동화된 RHEL 컨테이너 배포를 위한 사용자 지정 구성으로 바꿉니다. 이 프로세스는 Kickstart 파일을 사용자 지정하는 것과 유사하지만, Docker 의 선언적 접근 방식을 사용하여 컨테이너 환경 및 서비스를 정의합니다.
먼저, 프로젝트 디렉토리에 있는지 확인합니다.
cd ~/project
이제 rhel9-automated.dockerfile의 전체 내용을 바꿔서 완전한 사용자 지정 Dockerfile 을 생성합니다.
cat > rhel9-automated.dockerfile << 'EOF'
## RHEL 9 Automated Container Deployment
## Based on Red Hat Universal Base Image 9
FROM redhat/ubi9:latest
## Container metadata
LABEL maintainer="LabEx Admin"
LABEL description="Automated RHEL 9 container with web services"
LABEL version="1.0"
## System locale and timezone configuration
ENV LANG=en_US.UTF-8
ENV TZ=America/New_York
ENV CONTAINER_USER=labex
ENV CONTAINER_UID=1001
## Package installation and system configuration
RUN dnf update -y \
&& dnf install -y --allowerasing \
httpd \
curl \
tar \
gzip \
&& dnf clean all \
&& rm -rf /var/cache/dnf
## Create non-root user for security
RUN useradd -u ${CONTAINER_UID} -m -s /bin/bash ${CONTAINER_USER} \
&& echo "${CONTAINER_USER}:labex" | chpasswd
## Configure Apache for container environment
RUN sed -i 's/Listen 80/Listen 8080/' /etc/httpd/conf/httpd.conf \
&& chown -R ${CONTAINER_USER}:${CONTAINER_USER} /var/log/httpd /var/run/httpd
## Create startup script
RUN echo '#!/bin/bash' > /start.sh \
&& echo 'echo "RHEL Container started on $(date)" > /var/www/html/index.html' >> /start.sh \
&& echo 'echo "<h1>RHEL 9 Container</h1><p>Deployed via automated configuration</p>" >> /var/www/html/index.html' >> /start.sh \
&& echo 'exec /usr/sbin/httpd -D FOREGROUND' >> /start.sh \
&& chmod +x /start.sh
## Switch to non-root user
USER ${CONTAINER_USER}
## Expose port and define startup
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/ || exit 1
CMD ["/start.sh"]
EOF
이제 이 사용자 지정 Dockerfile 의 구조를 살펴보고 각 섹션이 Kickstart 구성의 다른 부분과 동일한 목적을 어떻게 수행하는지 이해해 보겠습니다.
1. 기본 이미지 및 메타데이터 섹션:
## RHEL 9 Automated Container Deployment
## Based on Red Hat Universal Base Image 9
FROM redhat/ubi9:latest
## Container metadata
LABEL maintainer="LabEx Admin"
LABEL description="Automated RHEL 9 container with web services"
LABEL version="1.0"
이 섹션은 Kickstart 파일에서 설치 미디어 및 기본 시스템 정보를 지정하는 것과 같습니다. FROM 지시어는 기본 RHEL 이미지를 지정하고, LABEL 지시어는 컨테이너에 대한 메타데이터를 제공합니다.
2. 환경 구성:
## System locale and timezone configuration
ENV LANG=en_US.UTF-8
ENV TZ=America/New_York
ENV CONTAINER_USER=labex
ENV CONTAINER_UID=1001
이는 Kickstart 파일의 timezone 및 lang 지시어와 유사합니다. 시스템 로캘, 시간대, 사용자 생성을 위한 변수를 설정하고 있습니다.
3. 패키지 설치:
## Package installation and system configuration
RUN dnf update -y \
&& dnf install -y --allowerasing \
httpd \
curl \
tar \
gzip \
&& dnf clean all \
&& rm -rf /var/cache/dnf
이 섹션은 Kickstart 의 %packages 섹션과 동일한 기능을 수행합니다. 시스템을 업데이트하고, 충돌을 처리하기 위해 --allowerasing을 사용하여 필요한 패키지를 설치하고, 이미지 크기를 줄이기 위해 패키지 캐시를 정리합니다.
4. 사용자 및 보안 구성:
## Create non-root user for security
RUN useradd -u ${CONTAINER_UID} -m -s /bin/bash ${CONTAINER_USER} \
&& echo "${CONTAINER_USER}:labex" | chpasswd
## Configure Apache for container environment
RUN sed -i 's/Listen 80/Listen 8080/' /etc/httpd/conf/httpd.conf \
&& chown -R ${CONTAINER_USER}:${CONTAINER_USER} /var/log/httpd /var/run/httpd
이는 Kickstart 의 user 지시어 및 설치 후 구성과 유사합니다. 보안을 위해 비 루트 사용자를 생성하고, 컨테이너 환경에 적합하도록 Apache 를 포트 8080 에서 실행하도록 구성합니다.
5. 시작 구성:
## Create startup script
RUN echo '#!/bin/bash' > /start.sh \
&& echo 'echo "RHEL Container started on $(date)" > /var/www/html/index.html' >> /start.sh \
&& echo 'echo "<h1>RHEL 9 Container</h1><p>Deployed via automated configuration</p>" >> /var/www/html/index.html' >> /start.sh \
&& echo 'exec /usr/sbin/httpd -D FOREGROUND' >> /start.sh \
&& chmod +x /start.sh
## Switch to non-root user
USER ${CONTAINER_USER}
## Expose port and define startup
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/ || exit 1
CMD ["/start.sh"]
이 마지막 섹션은 Kickstart 의 %post 섹션 및 서비스 구성과 같습니다. 시작 스크립트를 생성하고, 비 루트 사용자로 전환하고, 웹 서비스 포트를 노출하고, 상태 검사를 정의하고, 컨테이너의 시작 명령을 지정합니다.
새 Dockerfile 이 올바르게 생성되었는지 확인합니다.
cat rhel9-automated.dockerfile
기존 Kickstart 자동화 개념이 최신 컨테이너 기반 RHEL 배포로 어떻게 변환되는지 보여주는 전체 Dockerfile 내용을 볼 수 있습니다.
컨테이너 구성 검증 및 이미지 빌드
이 단계에서는 사용자 지정 Dockerfile 의 유효성을 검사하고 RHEL 컨테이너 이미지를 빌드합니다. 이 프로세스는 ksvalidator를 사용하여 Kickstart 파일의 유효성을 검사하는 것과 유사하지만, 빌드 프로세스 중에 Docker 의 내장된 유효성 검사를 사용합니다. Docker 는 구문을 확인하고 각 명령을 실행하려고 시도하여 문제에 대한 즉각적인 피드백을 제공합니다.
먼저, Dockerfile 이 있는 프로젝트 디렉토리에 있는지 확인합니다.
cd ~/project
빌드하기 전에 Dockerfile 구조를 검사하여 기본적인 구문 검사를 수행해 보겠습니다. Docker 는 빌드하지 않고 기본 구문의 유효성을 검사하는 방법을 제공합니다.
docker build --no-cache --progress=plain -f rhel9-automated.dockerfile -t rhel9-test:validation . --target ""
그러나 가장 효과적인 유효성 검사는 실제로 이미지를 빌드하는 것입니다. 구문 오류 또는 명령 관련 문제가 있는 경우 Docker 는 빌드 프로세스 중에 이를 보고합니다. 기본 UBI9 이미지에 존재하는 curl과 curl-minimal 간의 패키지 충돌을 처리하기 위해 dnf install과 함께 --allowerasing 플래그를 사용합니다.
docker build -t rhel9-automated:latest -f rhel9-automated.dockerfile .
빌드 프로세스의 각 단계를 보여주는 출력을 볼 수 있습니다.
[+] Building 45.2s (12/12) FINISHED
=> [internal] load build definition from rhel9-automated.dockerfile
=> => transferring dockerfile: 1.23kB
=> [internal] load .dockerignore
=> => transferring context: 2B
=> [internal] load metadata for docker.io/redhat/ubi9:latest
=> [1/8] FROM docker.io/redhat/ubi9:latest@sha256:...
=> [2/8] RUN dnf update -y && dnf install -y --allowerasing httpd curl tar gzip && dnf clean all && rm -rf /var/cache/dnf
=> [3/8] RUN useradd -u 1001 -m -s /bin/bash labex && echo "labex:labex" | chpasswd
=> [4/8] RUN sed -i 's/Listen 80/Listen 8080/' /etc/httpd/conf/httpd.conf && chown -R labex:labex /var/log/httpd /var/run/httpd
=> [5/8] RUN echo '#!/bin/bash' > /start.sh && echo 'echo "RHEL Container started on $(date)" > /var/www/html/index.html' >> /start.sh && echo 'echo "<h1>RHEL 9 Container</h1><p>Deployed via automated configuration</p>" >> /var/www/html/index.html' >> /start.sh && echo 'exec /usr/sbin/httpd -D FOREGROUND' >> /start.sh && chmod +x /start.sh
=> [6/8] USER labex
=> exporting to image
=> => exporting layers
=> => writing image sha256:a1b2c3d4e5f6...
=> => naming to docker.io/library/rhel9-automated:latest
빌드가 성공적으로 완료되면 Dockerfile 구문이 올바르고 모든 명령이 제대로 실행되었음을 의미합니다. 빌드 프로세스 중에 로캘 설정 및 구독 관리에 대한 몇 가지 경고 메시지가 표시될 수 있습니다. 이는 UBI 컨테이너에 일반적이며 기능에 영향을 미치지 않습니다.
이미지가 생성되었는지 확인합니다.
docker images rhel9-automated
새로 빌드된 이미지를 볼 수 있습니다.
REPOSITORY TAG IMAGE ID CREATED SIZE
rhel9-automated latest a1b2c3d4e5f6 2 minutes ago 280MB
이제 컨테이너가 예상대로 작동하는지 테스트해 보겠습니다. 분리된 모드에서 컨테이너를 실행합니다.
docker run -d --name rhel9-test -p 8080:8080 rhel9-automated:latest
컨테이너가 실행 중인지 확인합니다.
docker ps
목록에 컨테이너가 표시됩니다.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a1b2c3d4e5f6 rhel9-automated:latest "/start.sh" 30 seconds ago Up 30 seconds 0.0.0.0:8080->8080/tcp rhel9-test
컨테이너에 요청을 보내 웹 서비스를 테스트합니다.
curl http://localhost:8080
자동화된 RHEL 컨테이너의 HTML 출력을 볼 수 있습니다.
RHEL Container started on Wed Jul 22 14:30:15 UTC 2024
<h1>RHEL 9 Container</h1><p>Deployed via automated configuration</p>
마지막으로, 테스트 컨테이너를 중지하고 제거하여 정리합니다.
docker stop rhel9-test
docker rm rhel9-test
RHEL 컨테이너 구성이 성공적으로 유효성을 검사하고 테스트되었으며, 자동화된 배포 기능을 보여줍니다.
자동 배포 스크립트 생성
이 단계에서는 RHEL 컨테이너를 일관되고 반복적으로 배포하는 방법을 보여주는 자동화 스크립트를 생성합니다. 이 스크립트는 VM 자동화를 위해 Kickstart 파일을 사용하는 것과 동일한 역할을 하지만, 컨테이너화된 RHEL 배포에 맞게 조정되었습니다. 이 스크립트는 이미지 빌드, 컨테이너 배포 및 기본적인 상태 검사를 처리합니다.
먼저, 프로젝트 디렉토리에 있는지 확인합니다.
cd ~/project
Kickstart 및 virt-install을 사용하여 달성할 수 있는 자동화 기능을 모방하는 배포 자동화 스크립트를 생성합니다.
nano deploy-rhel-container.sh
포괄적인 배포 스크립트를 생성하려면 다음 내용을 추가합니다.
#!/bin/bash
## RHEL 컨테이너 자동 배포 스크립트
## 이 스크립트는 컨테이너 기반 RHEL 배포 자동화를 시연합니다.
## VM용 Kickstart 자동화와 유사하지만 컨테이너용입니다.
set -e ## 오류 발생 시 종료
## 구성 변수
IMAGE_NAME="rhel9-automated"
IMAGE_TAG="latest"
CONTAINER_NAME="rhel9-production"
HOST_PORT="8080"
CONTAINER_PORT="8080"
DOCKERFILE="rhel9-automated.dockerfile"
## 출력용 색상 코드
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' ## 색상 없음
## 색상 출력을 인쇄하는 함수
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
## Docker가 실행 중인지 확인하는 함수
check_docker() {
print_status "Docker 가용성 확인..."
if ! docker info > /dev/null 2>&1; then
print_error "Docker가 실행 중이거나 액세스할 수 없습니다."
exit 1
fi
print_success "Docker를 사용할 수 있습니다."
}
## 이미지를 빌드하는 함수
build_image() {
print_status "RHEL 컨테이너 이미지 빌드..."
if [ ! -f "$DOCKERFILE" ]; then
print_error "Dockerfile '$DOCKERFILE'을(를) 찾을 수 없습니다."
exit 1
fi
docker build -t "${IMAGE_NAME}:${IMAGE_TAG}" -f "$DOCKERFILE" .
print_success "이미지 '${IMAGE_NAME}:${IMAGE_TAG}'가 성공적으로 빌드되었습니다."
}
## 기존 컨테이너를 중지하고 제거하는 함수
cleanup_existing() {
print_status "기존 컨테이너 확인..."
if docker ps -a | grep -q "$CONTAINER_NAME"; then
print_warning "기존 컨테이너 '$CONTAINER_NAME'을(를) 중지하고 제거합니다."
docker stop "$CONTAINER_NAME" > /dev/null 2>&1 || true
docker rm "$CONTAINER_NAME" > /dev/null 2>&1 || true
fi
}
## 컨테이너를 배포하는 함수
deploy_container() {
print_status "RHEL 컨테이너 배포..."
docker run -d \
--name "$CONTAINER_NAME" \
-p "${HOST_PORT}:${CONTAINER_PORT}" \
--restart unless-stopped \
"${IMAGE_NAME}:${IMAGE_TAG}"
print_success "컨테이너 '$CONTAINER_NAME'이(가) 성공적으로 배포되었습니다."
}
## 배포를 확인하는 함수
verify_deployment() {
print_status "컨테이너 배포 확인..."
## 컨테이너가 시작될 때까지 대기
sleep 5
## 컨테이너가 실행 중인지 확인
if ! docker ps | grep -q "$CONTAINER_NAME"; then
print_error "컨테이너가 실행 중이 아닙니다."
docker logs "$CONTAINER_NAME"
exit 1
fi
## 웹 서비스가 응답하는지 확인
print_status "웹 서비스 테스트..."
for i in {1..10}; do
if curl -s "http://localhost:${HOST_PORT}" > /dev/null; then
print_success "웹 서비스가 응답합니다."
break
fi
if [ $i -eq 10 ]; then
print_error "10번 시도 후에도 웹 서비스가 응답하지 않습니다."
exit 1
fi
sleep 2
done
}
## 배포 정보를 표시하는 함수
show_deployment_info() {
print_success "=== RHEL 컨테이너 배포 완료 ==="
echo "컨테이너 이름: $CONTAINER_NAME"
echo "이미지: ${IMAGE_NAME}:${IMAGE_TAG}"
echo "포트 매핑: ${HOST_PORT}:${CONTAINER_PORT}"
echo "액세스 URL: http://localhost:${HOST_PORT}"
echo ""
print_status "컨테이너 상태:"
docker ps | grep "$CONTAINER_NAME"
echo ""
print_status "샘플 내용:"
curl -s "http://localhost:${HOST_PORT}" | head -2
}
## 주 배포 프로세스
main() {
echo "=== RHEL 컨테이너 자동 배포 ==="
echo "이 스크립트는 RHEL 컨테이너 배포를 자동화합니다."
echo "기존 설치를 위한 Kickstart 자동화와 유사합니다."
echo ""
check_docker
build_image
cleanup_existing
deploy_container
verify_deployment
show_deployment_info
print_success "자동화된 RHEL 컨테이너 배포가 성공적으로 완료되었습니다!"
}
## 스크립트 인수 처리
case "${1:-deploy}" in
"deploy" | "")
main
;;
"cleanup")
print_status "배포 정리..."
cleanup_existing
docker rmi "${IMAGE_NAME}:${IMAGE_TAG}" 2> /dev/null || true
print_success "정리가 완료되었습니다."
;;
"status")
docker ps | grep "$CONTAINER_NAME" || print_warning "컨테이너가 실행 중이 아님"
;;
*)
echo "사용법: $0 [deploy|cleanup|status]"
echo " deploy - RHEL 컨테이너를 빌드하고 배포합니다(기본값)."
echo " cleanup - 컨테이너를 중지하고 이미지를 제거합니다."
echo " status - 컨테이너 상태를 표시합니다."
exit 1
;;
esac
파일을 저장하고 nano 를 종료합니다 (Ctrl+X, 다음 Y, 다음 Enter).
배포 스크립트 구조 이해
스크립트를 실행하기 전에 이 자동화 스크립트가 어떻게 작동하는지 이해해 보겠습니다. 이 섹션에서는 각 구성 요소에 대한 자세한 설명을 제공하여 초보자가 셸 스크립팅 및 컨테이너 자동화 개념을 더 쉽게 이해할 수 있도록 합니다.
스크립트 헤더 및 오류 처리
#!/bin/bash
set -e ## 오류 발생 시 종료
#!/bin/bash: 이것은 "shebang"이라고 불리며, 시스템에 이 스크립트를 실행하기 위해 Bash 셸을 사용하도록 지시합니다.set -e: 이 명령은 명령이 실패하면 스크립트가 즉시 종료되도록 하여 잠재적으로 손상된 상태로 계속 진행하는 대신 첫 번째 오류에서 스크립트가 중지되도록 합니다.
구성 변수
## 구성 변수
IMAGE_NAME="rhel9-automated"
IMAGE_TAG="latest"
CONTAINER_NAME="rhel9-production"
HOST_PORT="8080"
CONTAINER_PORT="8080"
DOCKERFILE="rhel9-automated.dockerfile"
이러한 변수는 배포에 대한 모든 주요 매개변수를 정의합니다. 이러한 변수를 맨 위에 배치하면 스크립트 논리를 변경하지 않고도 배포 구성을 쉽게 수정할 수 있습니다. 이는 Kickstart 파일이 구성 매개변수를 사용하는 방식과 유사합니다.
사용자 친화적인 출력 시스템
## 출력용 색상 코드
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' ## 색상 없음
## 색상 출력을 인쇄하는 함수
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
이는 색상 출력을 사용하여 전문적인 로깅 시스템을 생성합니다.
\033[0;31m: 색상에 대한 ANSI 이스케이프 코드 (31 = 빨간색, 32 = 녹색 등)echo -e:-e플래그는 색상에 대한 백슬래시 이스케이프의 해석을 활성화합니다.$1: 함수에 전달된 첫 번째 인수를 참조합니다.
핵심 배포 기능
1. Docker 환경 확인
check_docker() {
print_status "Docker 가용성 확인..."
if ! docker info > /dev/null 2>&1; then
print_error "Docker가 실행 중이거나 액세스할 수 없습니다."
exit 1
fi
print_success "Docker를 사용할 수 있습니다."
}
docker info > /dev/null 2>&1:docker info를 실행하고 출력 (>) 과 오류 (2>&1) 를 모두/dev/null로 리디렉션합니다 (폐기).!: 결과를 부정합니다.docker info가 실패하면 (0 이 아닌 값을 반환) 조건이 참이 됩니다.- 이는 기존 VM 배포에서 가상화가 가능한지 확인하는 것과 같습니다.
2. 이미지 빌드 기능
build_image() {
print_status "RHEL 컨테이너 이미지 빌드..."
if [ ! -f "$DOCKERFILE" ]; then
print_error "Dockerfile '$DOCKERFILE'을(를) 찾을 수 없습니다."
exit 1
fi
docker build -t "${IMAGE_NAME}:${IMAGE_TAG}" -f "$DOCKERFILE" .
print_success "이미지 '${IMAGE_NAME}:${IMAGE_TAG}'가 성공적으로 빌드되었습니다."
}
[ ! -f "$DOCKERFILE" ]: Dockerfile 이 존재하지 않는지 테스트합니다 (!는 부정을 나타내고,-f는 파일 존재 여부를 테스트합니다).docker build -t: 태그 (이름:버전) 가 있는 컨테이너 이미지를 생성합니다.- 이는 ISO 미디어에서 기존 설치 프로세스를 대체합니다.
3. 정리 기능
cleanup_existing() {
print_status "기존 컨테이너 확인..."
if docker ps -a | grep -q "$CONTAINER_NAME"; then
print_warning "기존 컨테이너 '$CONTAINER_NAME'을(를) 중지하고 제거합니다."
docker stop "$CONTAINER_NAME" > /dev/null 2>&1 || true
docker rm "$CONTAINER_NAME" > /dev/null 2>&1 || true
fi
}
docker ps -a | grep -q: 모든 컨테이너를 나열하고 컨테이너 이름을 조용히 검색합니다.|| true: 컨테이너가 존재하지 않더라도 명령이 항상 성공하도록 합니다 (0 반환).- 이는 기존 배포와의 충돌을 방지합니다.
4. 컨테이너 배포
deploy_container() {
print_status "RHEL 컨테이너 배포..."
docker run -d \
--name "$CONTAINER_NAME" \
-p "${HOST_PORT}:${CONTAINER_PORT}" \
--restart unless-stopped \
"${IMAGE_NAME}:${IMAGE_TAG}"
print_success "컨테이너 '$CONTAINER_NAME'이(가) 성공적으로 배포되었습니다."
}
-d: 분리 모드 (백그라운드) 에서 컨테이너를 실행합니다.-p "${HOST_PORT}:${CONTAINER_PORT}": 호스트 포트를 컨테이너 포트에 매핑합니다.--restart unless-stopped: 컨테이너가 예기치 않게 중지되면 자동으로 다시 시작합니다 (수동 중지 제외).\: 여러 줄 명령에 대한 줄 연속 문자
5. 상태 확인
verify_deployment() {
print_status "컨테이너 배포 확인..."
## 컨테이너가 시작될 때까지 대기
sleep 5
## 컨테이너가 실행 중인지 확인
if ! docker ps | grep -q "$CONTAINER_NAME"; then
print_error "컨테이너가 실행 중이 아닙니다."
docker logs "$CONTAINER_NAME"
exit 1
fi
## 웹 서비스가 응답하는지 확인
print_status "웹 서비스 테스트..."
for i in {1..10}; do
if curl -s "http://localhost:${HOST_PORT}" > /dev/null; then
print_success "웹 서비스가 응답합니다."
break
fi
if [ $i -eq 10 ]; then
print_error "10번 시도 후에도 웹 서비스가 응답하지 않습니다."
exit 1
fi
sleep 2
done
}
{1..10}: Bash 중괄호 확장 - 1, 2, 3... 10 시퀀스를 생성합니다.curl -s: 무음 모드 HTTP 요청break: 서비스가 응답하면 루프를 조기에 종료합니다.- 이는 시간 초과와 함께 재시도 메커니즘을 구현합니다.
명령줄 인터페이스
case "${1:-deploy}" in
"deploy" | "")
main
;;
"cleanup")
print_status "배포 정리..."
cleanup_existing
docker rmi "${IMAGE_NAME}:${IMAGE_TAG}" 2> /dev/null || true
print_success "정리가 완료되었습니다."
;;
"status")
docker ps | grep "$CONTAINER_NAME" || print_warning "컨테이너가 실행 중이 아님"
;;
*)
echo "사용법: $0 [deploy|cleanup|status]"
exit 1
;;
esac
${1:-deploy}: 매개변수 확장 -$1(첫 번째 인수) 또는 "deploy"를 기본값으로 사용합니다.case문: 다른 언어의 switch/case와 유사합니다.;;: 각 case 분기를 종료합니다.$0: 스크립트 자체의 이름을 참조합니다.
이는 시스템 관리자가 배포, 유지 관리 및 모니터링에 다양한 도구를 사용하는 방식과 유사하게 여러 작업에 사용할 수 있는 다목적 스크립트를 생성합니다.
스크립트를 실행 가능하게 만듭니다.
chmod +x deploy-rhel-container.sh
이제 자동화된 배포 스크립트를 실행하여 전체 자동화 프로세스를 확인합니다.
./deploy-rhel-container.sh
전체 배포 프로세스를 보여주는 출력을 볼 수 있습니다.
=== RHEL 컨테이너 자동 배포 ===
이 스크립트는 RHEL 컨테이너 배포를 자동화합니다.
기존 설치를 위한 Kickstart 자동화와 유사합니다.
[INFO] Docker 가용성 확인...
[SUCCESS] Docker를 사용할 수 있습니다.
[INFO] RHEL 컨테이너 이미지 빌드...
[SUCCESS] 이미지 'rhel9-automated:latest'가 성공적으로 빌드되었습니다.
[INFO] 기존 컨테이너 확인...
[INFO] RHEL 컨테이너 배포...
[SUCCESS] 컨테이너 'rhel9-production'이(가) 성공적으로 배포되었습니다.
[INFO] 컨테이너 배포 확인...
[INFO] 웹 서비스 테스트...
[SUCCESS] 웹 서비스가 응답합니다.
[SUCCESS] === RHEL 컨테이너 배포 완료 ===
컨테이너 이름: rhel9-production
이미지: rhel9-automated:latest
포트 매핑: 8080:8080
액세스 URL: http://localhost:8080
다양한 스크립트 옵션을 테스트합니다.
./deploy-rhel-container.sh status
스크립트 실행 단계별 안내
스크립트를 실행하면 다음 시퀀스가 자동으로 실행됩니다.
1. 환경 유효성 검사 단계
스크립트는 먼저 Docker 를 사용할 수 있고 액세스할 수 있는지 확인합니다. 컨테이너 배포에는 작동하는 Docker 환경이 필요하므로 VM 배포에 작동하는 하이퍼바이저가 필요한 것과 유사합니다.
2. 이미지 빌드 단계
스크립트는 Dockerfile 에서 새 컨테이너 이미지를 빌드합니다. 이 프로세스는 다음과 같습니다.
rhel9-automated.dockerfile을 읽습니다.- 이미 존재하지 않는 경우 기본 UBI9 이미지를 다운로드합니다.
- Dockerfile 의 각 명령을 실행합니다.
rhel9-automated:latest로 태그된 새 이미지를 생성합니다.
3. 정리 단계
배포하기 전에 스크립트는 동일한 이름을 가진 기존 컨테이너가 있는지 확인하고 제거합니다. 이는 이름 충돌 없이 깨끗한 배포를 보장합니다.
4. 배포 단계
스크립트는 다음을 사용하여 새 컨테이너를 생성하고 시작합니다.
- 분리 모드: 컨테이너가 백그라운드에서 실행됩니다.
- 포트 매핑: 호스트 포트 8080 이 컨테이너 포트 8080 에 매핑됩니다.
- 다시 시작 정책: 컨테이너가 예기치 않게 중지되면 자동으로 다시 시작됩니다.
- 명명된 컨테이너: 쉬운 식별 및 관리
5. 확인 단계
스크립트는 성공적인 배포를 보장하기 위해 상태 검사를 수행합니다.
- 컨테이너 상태 확인: 컨테이너가 실행 중인지 확인합니다.
- 서비스 가용성 확인: HTTP 서비스 응답을 테스트합니다.
- 재시도 메커니즘: 2 초 간격으로 최대 10 번 시도합니다.
- 자동 오류 감지: 확인에 실패하면 오류와 함께 종료됩니다.
6. 정보 표시 단계
마지막으로 스크립트는 컨테이너 세부 정보, 액세스 URL 및 샘플 콘텐츠를 포함한 포괄적인 배포 정보를 표시합니다.
실제 사용 예
이 스크립트를 다양한 방식으로 사용할 수 있습니다.
정상적인 배포:
./deploy-rhel-container.sh
## 또는 명시적으로
./deploy-rhel-container.sh deploy
배포 상태 확인:
./deploy-rhel-container.sh status
리소스 정리:
./deploy-rhel-container.sh cleanup
스크립트 도움말 보기:
./deploy-rhel-container.sh help
기존 방법보다 유리한 점
이 자동화 방식은 기존 Kickstart + VM 배포에 비해 몇 가지 장점을 제공합니다.
- 속도: 컨테이너 시작은 일반적으로 VM 부팅보다 10-100 배 빠릅니다.
- 리소스 효율성: 컨테이너는 호스트 커널을 공유하여 메모리와 CPU 를 덜 사용합니다.
- 일관성: 동일한 컨테이너가 서로 다른 환경에서 동일하게 실행됩니다.
- 확장성: 여러 인스턴스를 쉽게 생성하거나 수평적으로 확장할 수 있습니다.
- 이식성: Docker 가 설치된 모든 시스템에서 실행할 수 있습니다.
- 버전 관리: 컨테이너 이미지를 버전 관리하고 레지스트리에 저장할 수 있습니다.
이 자동화 스크립트는 최신 컨테이너 기반 RHEL 배포가 기존 Kickstart 기반 VM 설치와 동일한 수준의 자동화 및 일관성을 달성할 수 있음을 보여주지만, 더 빠른 배포, 더 나은 리소스 활용, 최신 클라우드 환경에서 더 쉬운 확정과 같은 컨테이너화의 추가적인 이점을 제공합니다.
요약
이 Lab 에서는 Docker 컨테이너와 Red Hat Universal Base Images (UBI) 를 사용하여 Red Hat Enterprise Linux (RHEL) 9 배포를 자동화하는 최신 접근 방식을 살펴보았습니다. 기존 RHEL 배포가 Kickstart 파일과 가상 머신에 의존했던 반면, 최신 클라우드 네이티브 환경에서는 redhat/ubi9와 같은 UBI 이미지를 통해 컨테이너화된 RHEL 을 점점 더 많이 사용한다는 것을 알게 되었습니다.
시스템 구성, 패키지 설치, 사용자 관리 및 서비스 구성을 정의하는 사용자 지정 Dockerfile 을 생성하여 기존 Kickstart 개념을 컨테이너 자동화로 변환하는 연습을 했습니다. Kickstart 파일에 ksvalidator를 사용하는 대신, Docker 의 빌드 프로세스를 통해 컨테이너 구성을 검증하는 방법을 배웠으며, 이는 구문 및 실행 오류에 대한 즉각적인 피드백을 제공합니다.
마지막으로, VM 자동화를 위해 Kickstart 파일과 함께 virt-install을 사용하는 것과 유사하게, 엔드 투 엔드 컨테이너 배포를 시연하는 포괄적인 자동화 스크립트를 생성했습니다. 이 접근 방식은 기존 방법과 동일한 수준의 자동화 및 일관성을 제공하는 동시에 컨테이너화의 이점 (더 빠른 배포, 더 나은 리소스 활용, 이식성 및 최신 클라우드 환경에서 더 쉬운 확장) 을 제공합니다.



