Install and Automate RHEL Deployments

Red Hat Enterprise LinuxBeginner
Practice Now

Introduction

In this lab, you will learn the fundamentals of deploying and automating Red Hat Enterprise Linux (RHEL) 9 containers using Docker. Modern cloud-native deployments increasingly rely on containerized RHEL environments rather than traditional virtual machines. You will start by exploring Red Hat's Universal Base Images (UBI), which provide enterprise-grade RHEL environments in container format.

You will examine how traditional Kickstart concepts translate to container automation, create custom Dockerfiles that mirror installation configurations, and build automated deployment scripts. By the end of this lab, you will understand how to deploy RHEL containers efficiently and automate the process for consistent, repeatable deployments in modern cloud environments.

Explore Red Hat Universal Base Images (UBI)

In this step, you will explore Red Hat's Universal Base Images (UBI), which are enterprise-grade container images based on RHEL. Unlike traditional RHEL installations that require full virtual machines, UBI images provide RHEL environments in lightweight, portable containers. These images are freely redistributable and designed for modern cloud-native applications.

Red Hat provides several UBI variants optimized for different use cases. The redhat/ubi9 image provides a full RHEL-based container environment with the dnf package manager, making it suitable for applications requiring software installation and system automation.

First, let's examine the Red Hat container configuration template that has been prepared for this lab. This file demonstrates how traditional Kickstart concepts translate to container environments.

sudo cat /etc/labex/rhel-container-config.cfg

You will see output showing a Dockerfile-style configuration that mirrors traditional installation concepts:

## 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"]

Now, let's explore the available Red Hat UBI images. First, check if Docker is running and accessible:

docker --version

Pull the Red Hat UBI 9 image, which provides a full RHEL-based container environment:

docker pull redhat/ubi9

You should see output similar to:

Using default tag: latest
latest: Pulling from redhat/ubi9
Digest: sha256:...
Status: Downloaded newer image for redhat/ubi9:latest
docker.io/redhat/ubi9:latest

List the downloaded images to confirm the pull was successful:

docker images redhat/ubi9

The output will show details about the image:

REPOSITORY      TAG       IMAGE ID       CREATED      SIZE
redhat/ubi9     latest    b1c2d3e4f5g6   5 days ago   216MB

Now, let's run a basic container to explore the RHEL environment:

docker run -it --rm redhat/ubi9 /bin/bash

Inside the container, explore the RHEL environment by checking the operating system version:

cat /etc/redhat-release

You should see something like:

Red Hat Enterprise Linux release 9.6 (Plow)

Check the available package manager:

dnf --version

Exit the container by typing:

exit

Copy the template configuration to your project directory for customization:

sudo cp /etc/labex/rhel-container-config.cfg ~/project/rhel-container.dockerfile
sudo chown labex:labex ~/project/rhel-container.dockerfile

Verify the file was copied successfully:

ls -l ~/project/rhel-container.dockerfile

You have now successfully explored Red Hat UBI images and are ready to create custom container configurations in the next step.

Create a Custom RHEL Container Configuration

In this step, you will create a custom Dockerfile based on the Red Hat UBI image. This process mirrors how you would customize a Kickstart file for automated installations, but adapted for container environments. The Dockerfile serves as the automation template for creating consistent RHEL container deployments.

First, ensure you are in your project directory:

cd ~/project

Create a new, more specific Dockerfile for our automated RHEL container deployment:

cp rhel-container.dockerfile rhel9-automated.dockerfile

Verify both files exist:

ls -l *.dockerfile

You should see both files:

-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

Now, open the new Dockerfile to examine its structure before customization:

nano rhel9-automated.dockerfile

Inside the file, you'll see the container equivalent of Kickstart directives:

  • FROM directive: Specifies the base RHEL image (equivalent to installation media)
  • ENV directives: Set environment variables (equivalent to system configuration)
  • RUN directives: Execute commands during image build (equivalent to package installation)
  • EXPOSE and CMD: Configure services and startup (equivalent to service configuration)

For now, simply exit the editor by pressing Ctrl+X to proceed to the customization step.

Understanding this structure prepares you for the next step where you'll customize the container configuration to meet specific deployment requirements, just as you would customize a Kickstart file for automated VM installations.

Customize the Container Configuration for Automated Deployment

In this step, you will replace the contents of the rhel9-automated.dockerfile with a custom configuration for automated RHEL container deployment. This process parallels customizing a Kickstart file, but uses Docker's declarative approach to define the container environment and services.

First, ensure you are in the project directory:

cd ~/project

Now, create the complete custom Dockerfile by replacing the entire contents of rhel9-automated.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

Now let's examine the structure of this customized Dockerfile and understand how each section serves the same purpose as different parts of a Kickstart configuration:

1. Base Image and Metadata Section:

## 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"

This section is equivalent to specifying the installation media and basic system information in a Kickstart file. The FROM directive specifies our base RHEL image, while LABEL directives provide metadata about the container.

2. Environment Configuration:

## System locale and timezone configuration
ENV LANG=en_US.UTF-8
ENV TZ=America/New_York
ENV CONTAINER_USER=labex
ENV CONTAINER_UID=1001

This parallels the timezone and lang directives in Kickstart files. We're setting the system locale, timezone, and defining variables for user creation.

3. Package Installation:

## 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

This section performs the same function as the %packages section in Kickstart. We update the system, install required packages using --allowerasing to handle conflicts, and clean up package caches to reduce image size.

4. User and Security Configuration:

## 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

This mirrors the user directive and post-installation configuration in Kickstart. We create a non-root user for security and configure Apache to run on port 8080 (suitable for container environments).

5. Startup Configuration:

## 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"]

This final section is equivalent to the %post section and service configuration in Kickstart. We create a startup script, switch to the non-root user, expose the web service port, define health checks, and specify the container's startup command.

Verify the new Dockerfile was created correctly:

cat rhel9-automated.dockerfile

You should see the complete Dockerfile content that demonstrates how traditional Kickstart automation concepts translate to modern container-based RHEL deployments.

Validate the Container Configuration and Build the Image

In this step, you will validate your custom Dockerfile and build the RHEL container image. This process is similar to validating a Kickstart file with ksvalidator, but uses Docker's built-in validation during the build process. Docker will check the syntax and attempt to execute each instruction, providing immediate feedback on any issues.

First, ensure you are in the project directory where your Dockerfile is located:

cd ~/project

Before building, let's perform a basic syntax check by examining the Dockerfile structure. Docker provides a way to validate the basic syntax without building:

docker build --no-cache --progress=plain -f rhel9-automated.dockerfile -t rhel9-test:validation . --target ""

However, the most effective validation is to actually build the image. If there are syntax errors or issues with the commands, Docker will report them during the build process. Note that we use the --allowerasing flag with dnf install to handle package conflicts between curl and curl-minimal that exist in the base UBI9 image:

docker build -t rhel9-automated:latest -f rhel9-automated.dockerfile .

You should see output showing each step of the build process:

[+] 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

If the build completes successfully, it means your Dockerfile syntax is correct and all commands executed properly. You may see some warning messages about locale settings and subscription management during the build process - these are normal for UBI containers and don't affect functionality.

Verify the image was created:

docker images rhel9-automated

You should see your newly built image:

REPOSITORY       TAG       IMAGE ID       CREATED          SIZE
rhel9-automated  latest    a1b2c3d4e5f6   2 minutes ago    280MB

Now let's test the container to ensure it works as expected. Run the container in detached mode:

docker run -d --name rhel9-test -p 8080:8080 rhel9-automated:latest

Check if the container is running:

docker ps

You should see your container in the list:

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

Test the web service by making a request to the container:

curl http://localhost:8080

You should see the HTML output from your automated RHEL container:

RHEL Container started on Wed Jul 22 14:30:15 UTC 2024
<h1>RHEL 9 Container</h1><p>Deployed via automated configuration</p>

Finally, clean up by stopping and removing the test container:

docker stop rhel9-test
docker rm rhel9-test

Your RHEL container configuration has been successfully validated and tested, demonstrating automated deployment capabilities.

Create an Automated Deployment Script

In this step, you will create an automation script that demonstrates how to deploy RHEL containers consistently and repeatedly. This script serves the same purpose as using Kickstart files for VM automation but is adapted for containerized RHEL deployments. The script will handle image building, container deployment, and basic health checking.

First, ensure you are in your project directory:

cd ~/project

Create a deployment automation script that mimics the automation capabilities you would achieve with Kickstart and virt-install:

nano deploy-rhel-container.sh

Add the following content to create a comprehensive deployment script:

#!/bin/bash

## RHEL Container Automated Deployment Script
## This script demonstrates container-based RHEL deployment automation
## Similar to Kickstart automation for VMs, but for containers

set -e ## Exit on any error

## Configuration variables
IMAGE_NAME="rhel9-automated"
IMAGE_TAG="latest"
CONTAINER_NAME="rhel9-production"
HOST_PORT="8080"
CONTAINER_PORT="8080"
DOCKERFILE="rhel9-automated.dockerfile"

## Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' ## No Color

## Function to print colored output
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"
}

## Function to check if Docker is running
check_docker() {
  print_status "Checking Docker availability..."
  if ! docker info > /dev/null 2>&1; then
    print_error "Docker is not running or not accessible"
    exit 1
  fi
  print_success "Docker is available"
}

## Function to build the image
build_image() {
  print_status "Building RHEL container image..."
  if [ ! -f "$DOCKERFILE" ]; then
    print_error "Dockerfile '$DOCKERFILE' not found"
    exit 1
  fi

  docker build -t "${IMAGE_NAME}:${IMAGE_TAG}" -f "$DOCKERFILE" .
  print_success "Image '${IMAGE_NAME}:${IMAGE_TAG}' built successfully"
}

## Function to stop and remove existing container
cleanup_existing() {
  print_status "Checking for existing container..."
  if docker ps -a | grep -q "$CONTAINER_NAME"; then
    print_warning "Stopping and removing existing container '$CONTAINER_NAME'"
    docker stop "$CONTAINER_NAME" > /dev/null 2>&1 || true
    docker rm "$CONTAINER_NAME" > /dev/null 2>&1 || true
  fi
}

## Function to deploy the container
deploy_container() {
  print_status "Deploying RHEL container..."
  docker run -d \
    --name "$CONTAINER_NAME" \
    -p "${HOST_PORT}:${CONTAINER_PORT}" \
    --restart unless-stopped \
    "${IMAGE_NAME}:${IMAGE_TAG}"

  print_success "Container '$CONTAINER_NAME' deployed successfully"
}

## Function to verify deployment
verify_deployment() {
  print_status "Verifying container deployment..."

  ## Wait for container to start
  sleep 5

  ## Check if container is running
  if ! docker ps | grep -q "$CONTAINER_NAME"; then
    print_error "Container is not running"
    docker logs "$CONTAINER_NAME"
    exit 1
  fi

  ## Check if web service is responding
  print_status "Testing web service..."
  for i in {1..10}; do
    if curl -s "http://localhost:${HOST_PORT}" > /dev/null; then
      print_success "Web service is responding"
      break
    fi
    if [ $i -eq 10 ]; then
      print_error "Web service is not responding after 10 attempts"
      exit 1
    fi
    sleep 2
  done
}

## Function to display deployment information
show_deployment_info() {
  print_success "=== RHEL Container Deployment Complete ==="
  echo "Container Name: $CONTAINER_NAME"
  echo "Image: ${IMAGE_NAME}:${IMAGE_TAG}"
  echo "Port Mapping: ${HOST_PORT}:${CONTAINER_PORT}"
  echo "Access URL: http://localhost:${HOST_PORT}"
  echo ""
  print_status "Container Status:"
  docker ps | grep "$CONTAINER_NAME"
  echo ""
  print_status "Sample Content:"
  curl -s "http://localhost:${HOST_PORT}" | head -2
}

## Main deployment process
main() {
  echo "=== RHEL Container Automated Deployment ==="
  echo "This script automates RHEL container deployment"
  echo "Similar to Kickstart automation for traditional installations"
  echo ""

  check_docker
  build_image
  cleanup_existing
  deploy_container
  verify_deployment
  show_deployment_info

  print_success "Automated RHEL container deployment completed successfully!"
}

## Handle script arguments
case "${1:-deploy}" in
  "deploy" | "")
    main
    ;;
  "cleanup")
    print_status "Cleaning up deployment..."
    cleanup_existing
    docker rmi "${IMAGE_NAME}:${IMAGE_TAG}" 2> /dev/null || true
    print_success "Cleanup completed"
    ;;
  "status")
    docker ps | grep "$CONTAINER_NAME" || print_warning "Container not running"
    ;;
  *)
    echo "Usage: $0 [deploy|cleanup|status]"
    echo "  deploy  - Build and deploy RHEL container (default)"
    echo "  cleanup - Stop container and remove image"
    echo "  status  - Show container status"
    exit 1
    ;;
esac

Save the file and exit nano (Ctrl+X, then Y, then Enter).

Understanding the Deployment Script Structure

Before running the script, let's understand how this automation script works. This section provides a detailed explanation of each component, making it easier for beginners to understand shell scripting and container automation concepts.

Script Header and Error Handling

#!/bin/bash
set -e ## Exit on any error
  • #!/bin/bash: This is called a "shebang" - it tells the system to use the Bash shell to execute this script
  • set -e: This makes the script exit immediately if any command fails, ensuring the script stops at the first error rather than continuing with potentially broken state

Configuration Variables

## Configuration variables
IMAGE_NAME="rhel9-automated"
IMAGE_TAG="latest"
CONTAINER_NAME="rhel9-production"
HOST_PORT="8080"
CONTAINER_PORT="8080"
DOCKERFILE="rhel9-automated.dockerfile"

These variables define all the key parameters for our deployment. By putting them at the top, we can easily modify the deployment configuration without changing the script logic. This is similar to how Kickstart files use configuration parameters.

User-Friendly Output System

## Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' ## No Color

## Function to print colored output
print_status() {
  echo -e "${BLUE}[INFO]${NC} $1"
}

This creates a professional logging system with colored output:

  • \033[0;31m: ANSI escape codes for colors (31 = red, 32 = green, etc.)
  • echo -e: The -e flag enables interpretation of backslash escapes for colors
  • $1: Refers to the first argument passed to the function

Core Deployment Functions

1. Docker Environment Check

check_docker() {
  print_status "Checking Docker availability..."
  if ! docker info > /dev/null 2>&1; then
    print_error "Docker is not running or not accessible"
    exit 1
  fi
  print_success "Docker is available"
}
  • docker info > /dev/null 2>&1: Runs docker info and redirects both output (>) and errors (2>&1) to /dev/null (discards them)
  • !: Negates the result - if docker info fails (returns non-zero), the condition becomes true
  • This is equivalent to checking if virtualization is available in traditional VM deployments

2. Image Building Function

build_image() {
  print_status "Building RHEL container image..."
  if [ ! -f "$DOCKERFILE" ]; then
    print_error "Dockerfile '$DOCKERFILE' not found"
    exit 1
  fi

  docker build -t "${IMAGE_NAME}:${IMAGE_TAG}" -f "$DOCKERFILE" .
  print_success "Image '${IMAGE_NAME}:${IMAGE_TAG}' built successfully"
}
  • [ ! -f "$DOCKERFILE" ]: Tests if the Dockerfile does NOT exist (! negates, -f tests for file existence)
  • docker build -t: Creates a container image with a tag (name:version)
  • This replaces the traditional installation process from ISO media

3. Cleanup Function

cleanup_existing() {
  print_status "Checking for existing container..."
  if docker ps -a | grep -q "$CONTAINER_NAME"; then
    print_warning "Stopping and removing existing container '$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: Lists all containers and quietly searches for our container name
  • || true: Ensures the command always succeeds (returns 0) even if the container doesn't exist
  • This prevents conflicts with existing deployments

4. Container Deployment

deploy_container() {
  print_status "Deploying RHEL container..."
  docker run -d \
    --name "$CONTAINER_NAME" \
    -p "${HOST_PORT}:${CONTAINER_PORT}" \
    --restart unless-stopped \
    "${IMAGE_NAME}:${IMAGE_TAG}"

  print_success "Container '$CONTAINER_NAME' deployed successfully"
}
  • -d: Runs container in detached mode (background)
  • -p "${HOST_PORT}:${CONTAINER_PORT}": Maps host port to container port
  • --restart unless-stopped: Automatically restart container if it stops (except manual stops)
  • \: Line continuation character for multi-line commands

5. Health Verification

verify_deployment() {
  print_status "Verifying container deployment..."

  ## Wait for container to start
  sleep 5

  ## Check if container is running
  if ! docker ps | grep -q "$CONTAINER_NAME"; then
    print_error "Container is not running"
    docker logs "$CONTAINER_NAME"
    exit 1
  fi

  ## Check if web service is responding
  print_status "Testing web service..."
  for i in {1..10}; do
    if curl -s "http://localhost:${HOST_PORT}" > /dev/null; then
      print_success "Web service is responding"
      break
    fi
    if [ $i -eq 10 ]; then
      print_error "Web service is not responding after 10 attempts"
      exit 1
    fi
    sleep 2
  done
}
  • {1..10}: Bash brace expansion - creates sequence 1, 2, 3... 10
  • curl -s: Silent mode HTTP request
  • break: Exits the loop early when service responds
  • This implements a retry mechanism with timeout

Command-Line Interface

case "${1:-deploy}" in
  "deploy" | "")
    main
    ;;
  "cleanup")
    print_status "Cleaning up deployment..."
    cleanup_existing
    docker rmi "${IMAGE_NAME}:${IMAGE_TAG}" 2> /dev/null || true
    print_success "Cleanup completed"
    ;;
  "status")
    docker ps | grep "$CONTAINER_NAME" || print_warning "Container not running"
    ;;
  *)
    echo "Usage: $0 [deploy|cleanup|status]"
    exit 1
    ;;
esac
  • ${1:-deploy}: Parameter expansion - uses $1 (first argument) or "deploy" as default
  • case statement: Similar to switch/case in other languages
  • ;;: Terminates each case branch
  • $0: Refers to the script name itself

This creates a versatile script that can be used for multiple operations, similar to how system administrators use different tools for deployment, maintenance, and monitoring.

Make the script executable:

chmod +x deploy-rhel-container.sh

Now run the automated deployment script to see the complete automation process:

./deploy-rhel-container.sh

You should see output showing the complete deployment process:

=== RHEL Container Automated Deployment ===
This script automates RHEL container deployment
Similar to Kickstart automation for traditional installations

[INFO] Checking Docker availability...
[SUCCESS] Docker is available
[INFO] Building RHEL container image...
[SUCCESS] Image 'rhel9-automated:latest' built successfully
[INFO] Checking for existing container...
[INFO] Deploying RHEL container...
[SUCCESS] Container 'rhel9-production' deployed successfully
[INFO] Verifying container deployment...
[INFO] Testing web service...
[SUCCESS] Web service is responding
[SUCCESS] === RHEL Container Deployment Complete ===
Container Name: rhel9-production
Image: rhel9-automated:latest
Port Mapping: 8080:8080
Access URL: http://localhost:8080

Test the different script options:

./deploy-rhel-container.sh status

Script Execution Walkthrough

When you run the script, it executes the following sequence automatically:

1. Environment Validation Phase

The script first checks if Docker is available and accessible. This is crucial because container deployment requires a working Docker environment, similar to how VM deployment requires a working hypervisor.

2. Image Building Phase

The script builds a new container image from your Dockerfile. This process:

  • Reads the rhel9-automated.dockerfile
  • Downloads the base UBI9 image if not already present
  • Executes each instruction in the Dockerfile
  • Creates a new image tagged as rhel9-automated:latest

3. Cleanup Phase

Before deploying, the script checks for and removes any existing container with the same name. This ensures a clean deployment without naming conflicts.

4. Deployment Phase

The script creates and starts the new container with:

  • Detached mode: Container runs in the background
  • Port mapping: Host port 8080 maps to container port 8080
  • Restart policy: Container automatically restarts if it stops unexpectedly
  • Named container: Easy identification and management

5. Verification Phase

The script performs health checks to ensure successful deployment:

  • Container status check: Verifies the container is running
  • Service availability check: Tests HTTP service response
  • Retry mechanism: Attempts up to 10 times with 2-second intervals
  • Automatic failure detection: Exits with error if verification fails

6. Information Display Phase

Finally, the script displays comprehensive deployment information including container details, access URLs, and sample content.

Practical Usage Examples

You can use this script in various ways:

Normal deployment:

./deploy-rhel-container.sh
## or explicitly
./deploy-rhel-container.sh deploy

Check deployment status:

./deploy-rhel-container.sh status

Clean up resources:

./deploy-rhel-container.sh cleanup

View script help:

./deploy-rhel-container.sh help

Benefits Over Traditional Methods

This automation approach provides several advantages compared to traditional Kickstart + VM deployments:

  1. Speed: Container startup is typically 10-100x faster than VM boot
  2. Resource Efficiency: Containers share the host kernel, using less memory and CPU
  3. Consistency: Same container runs identically across different environments
  4. Scalability: Easy to create multiple instances or scale horizontally
  5. Portability: Can run on any system with Docker installed
  6. Version Control: Container images can be versioned and stored in registries

This automation script demonstrates how modern container-based RHEL deployments can achieve the same level of automation and consistency as traditional Kickstart-based VM installations, but with the added benefits of containerization such as faster deployment, better resource utilization, and easier scaling in modern cloud environments.

Summary

In this lab, you explored the modern approach to automating Red Hat Enterprise Linux (RHEL) 9 deployments using Docker containers and Red Hat Universal Base Images (UBI). You learned that while traditional RHEL deployments relied on Kickstart files and virtual machines, modern cloud-native environments increasingly use containerized RHEL through UBI images like redhat/ubi9.

You practiced translating traditional Kickstart concepts to container automation by creating custom Dockerfiles that define system configuration, package installation, user management, and service configuration. Instead of using ksvalidator for Kickstart files, you learned to validate container configurations through Docker's build process, which provides immediate feedback on syntax and execution errors.

Finally, you created a comprehensive automation script that demonstrates end-to-end container deployment, similar to using virt-install with Kickstart files for VM automation. This approach provides the same level of automation and consistency as traditional methods while offering the benefits of containerization: faster deployment, better resource utilization, portability, and easier scaling in modern cloud environments.