安装并自动化 RHEL 部署

Red Hat Enterprise LinuxBeginner
立即练习

介绍

在这个实验中,你将学习使用 Docker 部署和自动化 Red Hat Enterprise Linux (RHEL) 9 容器的基础知识。现代云原生部署越来越依赖于容器化的 RHEL 环境,而不是传统的虚拟机。你将首先探索 Red Hat 的通用基础镜像 (UBI),它以容器格式提供企业级的 RHEL 环境。

你将研究传统的 Kickstart 概念如何转化为容器自动化,创建反映安装配置的自定义 Dockerfile,并构建自动化部署脚本。通过完成这个实验,你将了解如何在现代云环境中高效地部署 RHEL 容器,并自动化该过程以实现一致、可重复的部署。

这是一个实验(Guided Lab),提供逐步指导来帮助你学习和实践。请仔细按照说明完成每个步骤,获得实际操作经验。根据历史数据,这是一个 初级 级别的实验,完成率为 89%。获得了学习者 98% 的好评率。

探索 Red Hat Universal Base Images (UBI)

在这一步,你将探索 Red Hat 的通用基础镜像 (UBI),它们是基于 RHEL 的企业级容器镜像。与需要完整虚拟机的传统 RHEL 安装不同,UBI 镜像在轻量级、可移植的容器中提供 RHEL 环境。这些镜像可以自由重新分发,并专为现代云原生应用程序设计。

Red Hat 提供了几个针对不同用例优化的 UBI 变体。 redhat/ubi9 镜像提供了一个完整的基于 RHEL 的容器环境,带有 dnf 包管理器,使其适用于需要软件安装和系统自动化的应用程序。

首先,让我们检查为这个实验准备的 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

拉取 Red Hat UBI 9 镜像,它提供了一个完整的基于 RHEL 的容器环境:

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 文件中的 timezonelang 指令类似。我们正在设置系统区域设置、时区,并定义用于用户创建的变量。

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 指令和安装后配置。我们创建一个非 root 用户以确保安全,并将 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 部分和服务配置。我们创建一个启动脚本,切换到非 root 用户,公开 Web 服务端口,定义健康检查,并指定容器的启动命令。

验证新的 Dockerfile 是否已正确创建:

cat rhel9-automated.dockerfile

你应该看到完整的 Dockerfile 内容,它演示了传统的 Kickstart 自动化概念如何转化为现代的基于容器的 RHEL 部署。

验证容器配置并构建镜像

在这一步,你将验证你的自定义 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 将在构建过程中报告它们。请注意,我们使用 --allowerasing 标志与 dnf install 一起处理基本 UBI9 镜像中存在的 curlcurl-minimal 之间的软件包冲突:

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

通过向容器发出请求来测试 Web 服务:

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 容器。该脚本的作用与使用 Kickstart 文件进行 VM 自动化相同,但适用于容器化的 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

  ## 检查 Web 服务是否响应
  print_status "正在测试 Web 服务..."
  for i in {1..10}; do
    if curl -s "http://localhost:${HOST_PORT}" > /dev/null; then
      print_success "Web 服务正在响应"
      break
    fi
    if [ $i -eq 10 ]; then
      print_error "Web 服务在 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)。

了解部署脚本结构

在运行脚本之前,让我们了解一下这个自动化脚本的工作原理。本节提供了对每个组件的详细说明,使初学者更容易理解 shell 脚本和容器自动化概念。

脚本标头和错误处理

#!/bin/bash
set -e ## 遇到任何错误时退出
  • #!/bin/bash:这被称为“shebang”——它告诉系统使用 Bash shell 来执行此脚本
  • 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 失败(返回非零),则条件变为真
  • 这相当于检查传统 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

  ## 检查 Web 服务是否响应
  print_status "正在测试 Web 服务..."
  for i in {1..10}; do
    if curl -s "http://localhost:${HOST_PORT}" > /dev/null; then
      print_success "Web 服务正在响应"
      break
    fi
    if [ $i -eq 10 ]; then
      print_error "Web 服务在 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] 正在测试 Web 服务...
[SUCCESS] Web 服务正在响应
[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 服务响应
  • 重试机制:尝试最多 10 次,间隔 2 秒
  • 自动故障检测:如果验证失败,则退出并显示错误

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 部署相比,这种自动化方法具有几个优势:

  1. 速度:容器启动通常比 VM 启动快 10-100 倍
  2. 资源效率:容器共享主机内核,使用更少的内存和 CPU
  3. 一致性:相同的容器在不同的环境中运行相同
  4. 可扩展性:易于创建多个实例或水平扩展
  5. 可移植性:可以在任何安装了 Docker 的系统上运行
  6. 版本控制:容器镜像可以进行版本控制并存储在注册表中

此自动化脚本演示了基于现代容器的 RHEL 部署如何实现与基于传统 Kickstart 的 VM 安装相同级别的自动化和一致性,但具有容器化的额外好处,例如更快的部署、更好的资源利用率以及在现代云环境中更轻松的扩展。

总结

在这个实验中,你探索了使用 Docker 容器和 Red Hat Universal Base Images (UBI) 自动化 Red Hat Enterprise Linux (RHEL) 9 部署的现代方法。你了解到,虽然传统的 RHEL 部署依赖于 Kickstart 文件和虚拟机,但现代云原生环境越来越多地通过 UBI 镜像(如 redhat/ubi9)使用容器化的 RHEL。

你通过创建自定义 Dockerfile 来练习将传统的 Kickstart 概念转换为容器自动化,这些 Dockerfile 定义了系统配置、软件包安装、用户管理和服务配置。你学习了如何通过 Docker 的构建过程验证容器配置,而不是使用 ksvalidator 进行 Kickstart 文件验证,这提供了关于语法和执行错误的即时反馈。

最后,你创建了一个全面的自动化脚本,该脚本演示了端到端的容器部署,类似于将 virt-install 与 Kickstart 文件一起用于 VM 自动化。这种方法提供了与传统方法相同级别的自动化和一致性,同时提供了容器化的好处:更快的部署、更好的资源利用率、可移植性以及在现代云环境中更轻松的扩展。