RHEL 에서 Podman 으로 컨테이너 실행

Red Hat Enterprise LinuxBeginner
지금 연습하기

소개

이 랩에서는 Red Hat Enterprise Linux (RHEL) 에서 Podman 을 사용하여 다중 계층 웹 애플리케이션을 배포하는 방법을 배우게 됩니다. 백엔드 역할을 하는 MariaDB 데이터베이스 컨테이너와 프론트엔드 역할을 하는 Apache 웹 서버 컨테이너를 배포하여 완전한 솔루션을 구축할 것입니다. 이 실습을 통해 초기 구성부터 서비스를 공개적으로 액세스 가능하게 만드는 것까지, 컨테이너화된 애플리케이션 배포의 필수 단계를 안내받게 됩니다.

먼저 MariaDB 컨테이너를 실행하고 환경 변수를 사용하여 시작 시 구성합니다. 다음으로, 데이터베이스의 데이터 지속성을 보장하기 위해 영구 스토리지를 구성하고 컨테이너 통신을 위한 사용자 지정 네트워크를 생성합니다. 그런 다음 Apache 웹 서버를 배포하고, 연결을 테스트하기 위해 포트를 노출하며, 마지막으로 안정적이고 자동화된 운영을 위해 컨테이너를 systemd 서비스로 관리하는 방법을 배우게 됩니다.

이것은 가이드 실험입니다. 학습과 실습을 돕기 위한 단계별 지침을 제공합니다.각 단계를 완료하고 실무 경험을 쌓기 위해 지침을 주의 깊게 따르세요. 과거 데이터에 따르면, 이것은 초급 레벨의 실험이며 완료율은 93%입니다.학습자들로부터 98%의 긍정적인 리뷰율을 받았습니다.

환경 변수를 사용하여 MariaDB 데이터베이스 컨테이너 실행

이 단계에서는 컨테이너화된 애플리케이션을 실행하고 환경 변수를 사용하여 시작 시 구성하는 방법을 배우게 됩니다. 이는 컨테이너 관리의 기본적인 기술로, 유연하고 안전한 배포를 가능하게 합니다. 데이터베이스를 초기화하기 위해 여러 구성 매개변수가 필요한 공식 MariaDB 이미지를 예로 사용하겠습니다.

먼저, 올바른 작업 디렉토리에 있는지 확인하십시오. 이 랩의 모든 작업은 ~/project 디렉토리 내에서 수행됩니다.

cd ~/project

컨테이너를 실행하기 전에, 레지스트리에서 이미지를 명시적으로 가져오는 것이 좋습니다. 이렇게 하면 로컬에 올바른 버전이 있는지 확인할 수 있습니다. 일관성을 위해 이 랩에서는 mariadb:10.6 이미지를 사용합니다.

podman pull mariadb:10.6

docker 레지스트리에서 mariadb:10.6 이미지를 선택합니다.

이미지가 다운로드되고 추출되고 있음을 나타내는 출력을 볼 수 있습니다.

10.6: Pulling from library/mariadb
...
Status: Downloaded newer image for mariadb:10.6
docker.io/library/mariadb:10.6

이제 MariaDB 컨테이너를 실행할 수 있습니다. podman run 명령은 새 컨테이너를 생성하고 시작합니다. 몇 가지 플래그를 사용합니다.

  • -d: 컨테이너를 분리 모드 (백그라운드) 에서 실행합니다.
  • --name mariadb_server: 컨테이너에 기억하기 쉬운 이름을 할당합니다.
  • -e VARIABLE=value: 컨테이너 내부에 환경 변수를 설정합니다. MariaDB 이미지는 처음 시작할 때 데이터베이스를 구성하는 데 사용합니다.

MariaDB 컨테이너를 시작하려면 다음 명령을 실행하십시오. root 암호를 설정하고, webappdb라는 새 데이터베이스를 생성하며, 전용 사용자 webappuser를 생성합니다.

podman run -d \
  --name mariadb_server \
  -e MARIADB_ROOT_PASSWORD=supersecret \
  -e MARIADB_DATABASE=webappdb \
  -e MARIADB_USER=webappuser \
  -e MARIADB_PASSWORD=userpass \
  mariadb:10.6

이 명령은 컨테이너가 생성되었음을 확인하는 긴 컨테이너 ID 를 출력합니다.

a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2

컨테이너가 실행 중인지 확인하려면 podman ps 명령을 사용하십시오.

podman ps

실행 중인 컨테이너 목록에 mariadb_server가 표시되어야 합니다.

CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS      NAMES
a1b2c3d4e5f6   mariadb:10.6   "docker-entrypoint.s…"   15 seconds ago   Up 14 seconds   3306/tcp   mariadb_server

마지막으로, 컨테이너의 로그를 확인하여 제공한 환경 변수를 사용하여 데이터베이스가 올바르게 초기화되었는지 확인해 보겠습니다.

podman logs mariadb_server

로그를 스크롤합니다. 서버가 연결 준비가 되었음을 나타내는 줄을 찾고 있습니다. 이는 성공적인 시작을 확인합니다. 출력은 길지만, 마지막 부분 근처의 주요 성공 메시지는 다음과 같습니다.

...
2024-05-20 10:30:00+00:00 [Note] [Entrypoint]: /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/
...
2024-05-20 10:30:15+00:00 [Note] mariadbd: ready for connections.
Version: '10.6.x-MariaDB-1:10.6.x+maria~ubu2004'  socket: '/run/mysqld/mysqld.sock'  port: 3306  mariadb.org binary distribution

환경 변수를 사용하여 MariaDB 컨테이너를 성공적으로 시작하고 구성했습니다.

MariaDB 컨테이너의 영구 스토리지 구성

이 단계에서는 컨테이너에 영구 스토리지를 구성하는 방법을 배우게 됩니다. 기본적으로 컨테이너 내에서 생성된 모든 데이터는 컨테이너의 수명 주기에 묶인 쓰기 가능한 레이어에 저장됩니다. 컨테이너를 제거하면 해당 데이터가 모두 손실됩니다. 데이터베이스와 같은 상태 저장 애플리케이션의 경우 이는 이상적이지 않습니다. 이를 해결하기 위해 Podman 볼륨 또는 바인드 마운트를 사용하여 컨테이너와 독립적으로 호스트 파일 시스템에 데이터를 저장합니다.

먼저, 새로운 스토리지 구성으로 다시 시작할 것이므로 이전 단계에서 생성한 컨테이너를 제거해야 합니다.

실행 중인 mariadb_server 컨테이너를 중지합니다.

podman stop mariadb_server

명령이 수신되었음을 확인하는 컨테이너의 이름이 출력으로 표시됩니다.

mariadb_server

이제 중지된 컨테이너를 제거합니다.

podman rm mariadb_server

다시, 컨테이너의 이름이 다시 표시됩니다.

mariadb_server

다음으로, 호스트 머신의 ~/project 디렉토리 내에 디렉토리를 생성합니다. 이 디렉토리는 MariaDB 데이터베이스 파일을 보관합니다.

mkdir ~/project/mariadb_data

루트리스 모드에서 Podman 을 사용하는 경우, 마운트된 디렉토리에 대한 올바른 권한을 설정해야 합니다. MariaDB 컨테이너는 특정 사용자 (UID 999) 로 실행되므로 디렉토리에 액세스할 수 있는지 확인해야 합니다. --userns=keep-id 플래그를 사용하고 적절한 권한을 설정합니다.

chmod 755 ~/project/mariadb_data

이제 MariaDB 컨테이너를 다시 실행합니다. 이 명령은 이전 단계의 명령과 유사하지만, 사용자 네임스페이스 매핑을 제대로 처리하기 위해 -v 플래그와 --userns=keep-id가 추가되었습니다. -v 플래그는 호스트의 ~/project/mariadb_data 디렉토리를 컨테이너 내부의 /var/lib/mysql 디렉토리 (MariaDB 가 데이터를 저장하는 위치) 에 마운트합니다. $(pwd)/mariadb_data를 사용하여 podman 명령에 필요한 절대 경로를 제공합니다.

podman run -d \
  --name mariadb_server \
  --userns=keep-id \
  -e MARIADB_ROOT_PASSWORD=supersecret \
  -e MARIADB_DATABASE=webappdb \
  -e MARIADB_USER=webappuser \
  -e MARIADB_PASSWORD=userpass \
  -v $(pwd)/mariadb_data:/var/lib/mysql:Z \
  mariadb:10.6

볼륨 마운트의 :Z 접미사는 Podman 에게 SELinux 호환성을 위해 내용을 개인적이고 공유되지 않은 레이블로 다시 레이블링하도록 지시합니다.

컨테이너가 시작된 후, 데이터가 호스트 머신에 저장되고 있는지 확인할 수 있습니다. ~/project/mariadb_data 디렉토리의 내용을 나열합니다.

ls -l ~/project/mariadb_data

컨테이너의 데이터베이스 엔진이 초기화되었으므로 ~/project/mariadb_data 내부에 여러 파일과 디렉토리가 생성된 것을 볼 수 있습니다. 이는 데이터가 이제 영구적임을 확인합니다. 컨테이너를 제거하더라도 이 데이터는 유지됩니다.

total 110632
-rw-rw---- 1 labex labex    16384 May 20 10:45 aria_log.00000001
-rw-rw---- 1 labex labex       52 May 20 10:45 aria_log_control
-rw-rw---- 1 labex labex      983 May 20 10:45 ib_buffer_pool
-rw-rw---- 1 labex labex 12582912 May 20 10:45 ibdata1
-rw-rw---- 1 labex labex 50331648 May 20 10:45 ib_logfile0
-rw-rw---- 1 labex labex 50331648 May 20 10:45 ib_logfile1
drwx------ 2 labex labex     4096 May 20 10:45 mysql
drwx------ 2 labex labex     4096 May 20 10:45 performance_schema
drwx------ 2 labex labex     4096 May 20 10:45 sys
drwx------ 2 labex labex     4096 May 20 10:45 webappdb

MariaDB 컨테이너가 영구 스토리지를 사용하도록 성공적으로 구성하여 데이터베이스 데이터가 컨테이너 재시작 및 제거 후에도 유지되도록 했습니다.

사용자 정의 네트워크 생성 및 Apache 웹 서버 배포

이 단계에서는 컨테이너에 대한 사용자 정의 네트워크를 생성하고 Apache 웹 서버를 배포합니다. Podman 은 기본 네트워크를 제공하지만, 사용자 정의 네트워크를 사용하는 것이 가장 좋은 방법입니다. 이는 더 나은 격리를 제공하며, 가장 중요한 것은 컨테이너 간의 자동 DNS 확인을 가능하게 합니다. 이를 통해 컨테이너는 변경될 수 있는 IP 주소를 사용하는 것보다 더 안정적인 이름을 사용하여 서로 통신할 수 있습니다.

먼저, 애플리케이션을 위한 사용자 정의 브리지 네트워크를 생성해 보겠습니다. 이름을 webapp-network로 지정합니다.

podman network create webapp-network

이 명령은 새로 생성된 네트워크의 이름을 출력합니다.

webapp-network

Podman 네트워크를 모두 나열하여 사용자의 네트워크가 성공적으로 생성되었는지 확인할 수 있습니다.

podman network ls

기본 네트워크와 함께 목록에 webapp-network가 표시되어야 합니다.

NETWORK ID     NAME               DRIVER    SCOPE
...
f1e2d3c4b5a6   webapp-network     bridge    local
...

다음으로, 이 새 네트워크에서 mariadb_server 컨테이너를 다시 생성해야 합니다. 이 환경의 네트워크 백엔드 구성으로 인해 기존 컨테이너를 새 네트워크에 연결할 수 없습니다. 대신, 새 네트워크 구성으로 컨테이너를 중지하고 다시 생성합니다.

실행 중인 mariadb_server 컨테이너를 중지합니다.

podman stop mariadb_server

중지된 컨테이너를 제거합니다.

podman rm mariadb_server

이제 새 네트워크로 MariaDB 컨테이너를 다시 생성합니다. 이 명령은 이전 단계의 명령과 유사하지만, --network webapp-network 플래그가 추가되었습니다.

podman run -d \
  --name mariadb_server \
  --network webapp-network \
  --userns=keep-id \
  -e MARIADB_ROOT_PASSWORD=supersecret \
  -e MARIADB_DATABASE=webappdb \
  -e MARIADB_USER=webappuser \
  -e MARIADB_PASSWORD=userpass \
  -v $(pwd)/mariadb_data:/var/lib/mysql:Z \
  mariadb:10.6

이제 웹 서버를 배포해 보겠습니다. 공식 Apache httpd 이미지를 사용합니다. 먼저, 웹사이트 파일을 저장할 호스트에 디렉토리를 생성합니다.

mkdir ~/project/webapp_content

이 새 디렉토리에 간단한 index.html 파일을 생성합니다. 이것이 웹 애플리케이션의 홈페이지가 됩니다.

echo "<h1>Welcome to My Web App</h1>" > ~/project/webapp_content/index.html

Apache 컨테이너가 파일에 액세스할 수 있도록 웹앱 콘텐츠 디렉토리에 대한 올바른 권한을 설정합니다.

chmod 755 ~/project/webapp_content

이제 Apache httpd 컨테이너를 실행합니다. webapp-network에 연결하고 webapp_content 디렉토리를 볼륨으로 마운트합니다. 이렇게 하면 웹 서버가 방금 생성한 index.html 파일을 제공할 수 있습니다.

podman run -d \
  --name web_server \
  --network webapp-network \
  -v $(pwd)/webapp_content:/usr/local/apache2/htdocs/:Z \
  httpd:2.4

옵션을 자세히 살펴보겠습니다.

  • --network webapp-network: 새 컨테이너를 사용자 정의 네트워크에 연결합니다.
  • -v $(pwd)/webapp_content:/usr/local/apache2/htdocs/:Z: 로컬 webapp_content 디렉토리를 컨테이너의 /usr/local/apache2/htdocs/에 마운트합니다. 이는 Apache 가 파일을 제공하는 기본 디렉토리입니다. :Z 접미사는 Podman 에게 SELinux 호환성을 위해 내용을 개인적이고 공유되지 않은 레이블로 다시 레이블링하도록 지시합니다.

두 컨테이너가 모두 실행 중인지 확인합니다.

podman ps

이제 실행 중인 컨테이너 목록에 mariadb_serverweb_server가 모두 표시되어야 합니다.

CONTAINER ID  IMAGE                           COMMAND           CREATED         STATUS         PORTS       NAMES
6a3f46c0ab3a  docker.io/library/mariadb:10.6  mariadbd          29 seconds ago  Up 29 seconds  3306/tcp    mariadb_server
da5d52ce9c41  docker.io/library/httpd:2.4     httpd-foreground  7 seconds ago   Up 7 seconds   80/tcp      web_server

이제 두 컨테이너가 동일한 사용자 정의 네트워크에 있으며 이름으로 서로 통신할 수 있습니다.

웹 서버 포트 노출 및 연결 테스트

이 단계에서는 컨테이너의 포트를 호스트 머신에 노출하여 컨테이너의 격리된 네트워크 외부에서 서비스에 액세스할 수 있도록 하는 방법을 배우게 됩니다. Apache 웹 서버는 실행 중이지만, 아직 호스트의 브라우저나 명령줄에서 액세스할 수 없습니다. 컨테이너의 포트를 게시하여 이 문제를 해결할 것입니다.

포트 매핑은 컨테이너가 생성될 때 정의됩니다. 따라서 이전 단계에서 생성한 web_server 컨테이너를 먼저 중지하고 제거해야 합니다. 웹사이트 콘텐츠에 대해서는 걱정하지 마십시오. 바인드 마운트를 사용했으므로 호스트의 ~/project/webapp_content 디렉토리에 안전하게 보관됩니다.

먼저, 컨테이너를 중지합니다.

podman stop web_server
web_server

다음으로, 중지된 컨테이너를 제거합니다.

podman rm web_server
web_server

이제 web_server 컨테이너를 다시 실행하지만, 이번에는 호스트의 포트를 컨테이너의 포트에 매핑하기 위해 -p (또는 --publish) 플래그를 추가합니다. 호스트의 포트 8080을 컨테이너 내부의 포트 80 (기본 HTTP 포트) 에 매핑합니다.

podman run -d \
  --name web_server \
  --network webapp-network \
  -v $(pwd)/webapp_content:/usr/local/apache2/htdocs/:Z \
  -p 8080:80 \
  httpd:2.4

새로운 플래그 -p 8080:80은 Podman 에게 호스트의 포트 8080에서 들어오는 모든 트래픽을 web_server 컨테이너 내부의 포트 80으로 전달하도록 지시합니다.

podman ps를 사용하여 컨테이너가 실행 중이고 포트가 올바르게 매핑되었는지 확인해 보겠습니다.

podman ps

web_server 컨테이너의 PORTS 열을 확인하십시오. 이제 0.0.0.0:8080에서 80/tcp로의 매핑이 표시되어 포트가 성공적으로 노출되었음을 나타냅니다.

CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                  NAMES
c5d4e3f2a1b6   httpd:2.4      "httpd-foreground"       10 seconds ago   Up 9 seconds    0.0.0.0:8080->80/tcp   web_server
a1b2c3d4e5f6   mariadb:10.6   "docker-entrypoint.s…"   25 minutes ago   Up 25 minutes   3306/tcp               mariadb_server

마지막으로, curl 명령을 사용하여 호스트 머신에서 연결을 테스트해 보겠습니다. 이는 포트 8080에서 localhost로 HTTP 요청을 보냅니다.

curl http://localhost:8080

index.html 파일의 HTML 콘텐츠가 출력으로 표시되어 웹 서버가 이제 호스트에서 액세스할 수 있음을 확인해야 합니다.

<h1>Welcome to My Web App</h1>

컨테이너화된 웹 서버를 호스트 머신에 성공적으로 노출했습니다. 이는 애플리케이션을 사용자에게 제공하기 위한 중요한 단계입니다.

systemd 서비스로 웹 서버 컨테이너 관리

이 마지막 단계에서는 컨테이너가 자동으로 시작되도록 구성하여 서비스가 충돌이나 시스템 재부팅에 탄력적으로 대처할 수 있도록 하는 방법을 배우게 됩니다. 표준 Red Hat Enterprise Linux 시스템에서 systemd는 서비스를 관리하는 주요 도구입니다. 그러나 이 랩의 Podman 환경은 컨테이너를 직접 관리하기 위해 systemd를 사용하지 않습니다.

대신, Podman 의 내장된 **재시작 정책 (restart policies)**을 사용하여 동일한 결과 (자동 서비스 재시작) 를 얻을 것입니다. 이는 Podman 데몬에 의해 컨테이너가 자동으로 시작되도록 보장하는 표준 컨테이너 네이티브 방식입니다. 어떤 이유로든 중지되면 항상 재시작되도록 web_server를 구성합니다.

먼저, 재시작 정책은 컨테이너가 생성될 때만 적용할 수 있으므로 기존 컨테이너를 제거해야 합니다.

web_server 컨테이너를 중지합니다.

podman stop web_server
web_server

이제 제거합니다.

podman rm web_server
web_server

다음으로, 이전과 동일한 구성으로 web_server 컨테이너를 다시 생성하지만 --restart always 플래그를 추가합니다. 이 플래그는 Podman 데몬에게 컨테이너를 모니터링하고 종료될 경우 재시작하도록 지시합니다.

podman run -d \
  --name web_server \
  --network webapp-network \
  -v $(pwd)/webapp_content:/usr/local/apache2/htdocs/:Z \
  -p 8080:80 \
  --restart always \
  httpd:2.4

컨테이너는 평소대로 시작됩니다. 재시작 정책이 활성 상태인지 확인하려면 컨테이너의 구성을 검사할 수 있습니다.

podman inspect web_server --format '{{.HostConfig.RestartPolicy.Name}}'

이 명령은 always를 반환하여 정책이 설정되었음을 확인해야 합니다.

always

이제 시스템 재부팅 또는 컨테이너 실패 후 발생할 상황을 시뮬레이션하기 위해 컨테이너를 수동으로 재시작하여 재시작 정책이 작동하는 방식을 시연해 보겠습니다.

먼저, 현재 재시작 정책 구성을 확인해 보겠습니다.

podman inspect web_server --format '{{.HostConfig.RestartPolicy.Name}}'

이는 always를 표시하여 재시작 정책이 구성되었음을 확인해야 합니다.

always

이제 실패 후 복구를 시뮬레이션하기 위해 수동 재시작을 테스트해 보겠습니다.

podman start web_server
web_server

컨테이너가 실행 중인지 확인합니다.

podman ps

재시작 정책이 적용된 두 컨테이너가 실행 중인 것을 볼 수 있습니다.

CONTAINER ID   IMAGE          COMMAND                  CREATED              STATUS              PORTS                  NAMES
e7f6g5h4i3j2   httpd:2.4      "httpd-foreground"       About a minute ago   Up 5 seconds        0.0.0.0:8080->80/tcp   web_server
a1b2c3d4e5f6   mariadb:10.6   "docker-entrypoint.s…"   About an hour ago    Up About an hour    3306/tcp               mariadb_server

마지막으로, 서비스에 액세스할 수 있는지 확인합니다.

curl http://localhost:8080
<h1>Welcome to My Web App</h1>

재시작 정책 이해:

구성한 --restart always 정책은 다음을 보장합니다.

  • 컨테이너가 예기치 않게 종료되면 자동으로 재시작됩니다.
  • Podman 서비스가 시작될 때 (예: 시스템 재부팅 후) 컨테이너가 자동으로 시작됩니다.
  • 이는 프로덕션 배포에 대한 탄력성을 제공합니다.

참고: 일부 랩 환경에서는 Podman 구성 및 Podman 시스템 서비스 실행 여부에 따라 자동 재시작 동작이 다를 수 있습니다. 주요 학습 목표는 프로덕션 배포를 위해 재시작 정책을 구성하는 방법을 이해하는 것입니다.

컨테이너가 서비스처럼 관리되도록 성공적으로 구성하여 자동으로 사용할 수 있도록 했습니다. 이는 컨테이너화된 애플리케이션의 기본 라이프사이클 관리를 완료합니다.

요약

이 랩에서는 Podman 을 사용하여 RHEL 에 다중 컨테이너 웹 애플리케이션을 배포하는 기본적인 프로세스를 배웠습니다. 먼저, 런타임에 환경 변수를 전달하여 루트 암호, 새 데이터베이스 및 전용 사용자를 포함한 초기 상태를 구성하면서 MariaDB 데이터베이스 컨테이너를 실행했습니다. 그런 다음, 컨테이너 재시작 시에도 중요한 데이터가 보존되도록 데이터베이스 컨테이너에 대한 영구 스토리지를 구성했습니다.

애플리케이션 스택을 완료하기 위해 컨테이너 간의 안전하고 격리된 통신을 가능하게 하는 사용자 지정 네트워크를 생성했습니다. 이 네트워크에 Apache 웹 서버 컨테이너를 배포하고 외부 사용자 액세스를 허용하기 위해 해당 포트를 노출했습니다. 마지막으로, 웹 서버 컨테이너를 systemd 와 통합하여 부팅 시 자동으로 시작되고 안정적으로 실행되도록 시스템 서비스로 관리하여 프로덕션 준비 배포 패턴을 시연했습니다.