카테고리 보관물:  IT

CKAD Cronjob 문제 후기 – 20250618

개요

CKAD 시험에서 출제된 Kubernetes CronJob 문제 풀이를 기록합니다. 특정 스케줄로 동작하는 CronJob을 작성하고, 성공/실패 history 보관 수, activeDeadlineSeconds, restartPolicy 등 세부 옵션을 지정하며, 테스트용 Job도 별도로 생성합니다.

출제 문제

※ 기억에 의존해 복기하는 문제라 오류가 있을 수 있습니다.
참고만 부탁 드립니다.

  • 30분 마다 job을 스케쥴 처리할 수 있는 cronjob 생성
  • cronjob 이름은 grep, namespace는 devops 사용할 것
  • 성공 history는 64개, 실패 history는 160개 보관
  • job이 실행되고 8초 이내에 완료 되지 못하면 중단할 것
  • pod가 중단 되더라도 재실행 되지 않을 것
  • container 이름: busybox
    image: busybox:stable,
    command: [“grep”, “-i”, “NAMESERVER”, “/etc/resolv.conf”]
  • cronjob 테스트를 위해 job을 별도로 생성해볼 것
    job 이름: grep-test
    namespace: devops

해결 방법

# cronjob-grep.yaml 생성

apiVersion: batch/v1
kind: CronJob
metadata:
  name: grep
  namespace: devops
spec:
  schedule: "*/30 * * * *"
  successfulJobsHistoryLimit: 64
  failedJobsHistoryLimit: 160
  jobTemplate:
    spec:
      activeDeadlineSeconds: 8
      template:
        spec:
          containers:
          - name: busybox
            image: busybox:stable
            imagePullPolicy: IfNotPresent
            command: ["grep", "-i", "NAMESERVER", "/etc/resolv.conf"]
          restartPolicy: Never
# job-grep-test.yaml 생성

apiVersion: batch/v1
kind: Job
metadata:
  name: grep-test
  namespace: devops
spec:
  activeDeadlineSeconds: 8
  template:
    spec:
      containers:
      - name: busybox
        image: busybox:stable
        imagePullPolicy: IfNotPresent
        command: ["grep", "-i", "NAMESERVER", "/etc/resolv.conf"]
      restartPolicy: Never
# cronjob 생성 및 상태 확인
$ k apply -f cronjob-grep.yaml
$ k get cronjob -n devops
$ k describe cronjob -n devops grep

# job 생성 및 실행 상태 확인
$ k apply -f job-grep-test.yaml
$ k get job -n devops
$ k describe job -n devops grep-test
$ k get po -n devops # pod의 Completed 상태 확인

CronJob 주요 필드 설명

CronJob은 주기적으로 Job을 생성하는 Kubernetes 리소스입니다. spec.schedule은 Linux cron 문법(*/분 시 일 월 요일)을 따르며, */30 * * * *는 30분마다 실행을 의미합니다. 시험에서는 schedule 문법과 Job/Pod 레벨 옵션을 조합하는 문제가 자주 출제됩니다.

주요 필드와 역할은 다음과 같습니다. successfulJobsHistoryLimitfailedJobsHistoryLimit는 보관할 완료/실패 Job 수를 제한해 클러스터 리소스 낭비를 방지합니다. jobTemplate.spec.activeDeadlineSeconds는 Job 실행 시작 후 제한 시간이며, 이 시간을 초과하면 실행 중인 Pod를 강제 종료합니다. restartPolicy: Never는 Pod 실패 시 재시작하지 않고 새 Pod를 생성하는 방식으로, 반복 실행 가능한 Job에 적합합니다.

# CronJob 상태 및 다음 실행 시간 확인
kubectl get cronjob grep -n devops

# CronJob이 생성한 Job 목록 확인
kubectl get jobs -n devops

# 특정 Job이 생성한 Pod 확인 및 로그 조회
kubectl get pods -n devops --selector=job-name=grep-test
kubectl logs -n devops -l job-name=grep-test

CronJob을 즉시 테스트하려면 kubectl create job --from=cronjob/grep grep-test -n devops로 기존 CronJob 스펙을 기반으로 Job을 수동 실행할 수 있습니다. 이 방법이 YAML 파일을 별도로 작성하는 것보다 간결하고 오타 위험이 없어 시험 환경에서 유용합니다.

concurrencyPolicy와 startingDeadlineSeconds

CronJob에는 이전 Job이 아직 실행 중일 때 새 Job을 어떻게 처리할지 결정하는 concurrencyPolicy 필드가 있습니다. Allow(기본값)는 동시 실행을 허용하고, Forbid는 이전 Job이 완료될 때까지 새 Job 생성을 건너뜁니다. Replace는 실행 중인 Job을 취소하고 새 Job으로 교체합니다. Job 실행 시간이 스케줄 간격보다 길어질 수 있는 경우 Forbid를 설정해야 중복 실행으로 인한 리소스 낭비를 방지할 수 있습니다.

startingDeadlineSeconds는 스케줄된 시간을 놓쳤을 때 허용하는 지연 시간입니다. 클러스터가 일시적으로 중단되었다가 복구될 때, 누락된 스케줄을 이 시간 안에서 복구 실행합니다. 이 값을 설정하지 않으면 100번 이상 스케줄을 놓친 경우 CronJob 컨트롤러가 Job 생성을 포기하므로, 장기 유지보수 후 복구 시 Jobs가 실행되지 않는 상황에 주의해야 합니다.

참고

참고 문서: Kubernetes Job 공식 문서 · kubectl create cronjob

Let’s Encrypt 인증서 생성 실패

개요

certbot을 사용해 Let’s Encrypt 인증서 생성 실패가 발생하는 원인은 다양하지만, RHEL/Rocky Linux 계열의 서버에서는 SELinux가 certbot의 HTTP 인증 프로세스를 차단하여 실패하는 경우가 있습니다. Apache가 설치된 환경에서는 Apache 자체는 SELinux 정책에 자동 등록되지만, certbot이 도메인 검증 시 사용하는 80/443 포트 바인딩용 독립 프로세스는 SELinux 정책에 등록되지 않아 차단됩니다. 이 글에서는 오류 내용과 SELinux 비활성화를 통한 해결 방법을 정리합니다.

증상 — certbot 실행 오류

certbot으로 인증서 발급을 시도하면 webroot 플러그인 관련 오류가 발생하며 인증서 발급이 완료되지 않습니다.

@:~# sudo certbot certonly --cert-name  -d www.
...
certbot.errors.PluginError: Every requested domain must have a webroot when using the webroot plugin.
2025-01-01 01:13:52,404:ERROR:certbot._internal.log:Every requested domain must have a webroot when using the webroot plugin.

원인 분석 — SELinux 정책 미등록

SELinux가 enforcing 모드로 활성화된 경우, Apache처럼 사전에 SELinux 정책이 등록된 서비스는 정상 동작하지만, certbot이 도메인 인증 시 새로 바인딩하는 포트(80, 443)용 프로세스는 정책이 없어 차단됩니다. 이 때문에 certbot이 ACME challenge 응답을 처리하지 못하고 실패합니다.

# SELinux 현재 상태 확인
@:~# sestatus
SELinuxfs mount:                /sys/fs/selinux
SELinuxfs status:                 enabled
SELinux mount directory:         /etc/selinux
Loaded policy name:              targeted
Current mode:                    enforcing

해결 방법 — SELinux 비활성화

SELinux 정책을 개별 추가하는 방법도 있으나, 서버 환경에서 SELinux 제어가 불필요한 경우 비활성화 처리가 가장 빠른 해결책입니다. /etc/sysconfig/selinux 파일에서 SELINUX=disabled로 변경한 뒤 재부팅합니다.

@:~# vi /etc/sysconfig/selinux
SELINUX=disabled

@:~# init 6

# 재기동 이후 SELinux 상태 확인
@:~# sestatus -v
SELinux status:                 disabled

SELinux 영구 비활성화 vs 임시 비활성화

/etc/sysconfig/selinux를 수정하고 재부팅하는 방식은 영구 비활성화입니다. 재부팅 없이 즉시 permissive 모드(정책 위반은 로깅만 하고 차단하지 않음)로 전환하려면 setenforce 0을 사용합니다. Permissive 모드는 SELinux 정책 개발이나 문제 진단 시 유용하며, 재부팅하면 /etc/sysconfig/selinux에 설정된 모드로 복귀합니다.

# 현재 세션에서만 permissive로 전환 (재부팅 후 원복)
setenforce 0

# 다시 enforcing으로 전환
setenforce 1

# 현재 상태 확인
getenforce

SELinux를 완전히 비활성화하는 대신 certbot에 대한 SELinux 정책만 추가하는 방법도 있습니다. audit.log에서 certbot 관련 AVC 거부 메시지를 확인하고 audit2allow 도구로 커스텀 정책 모듈을 생성할 수 있습니다. 그러나 프로덕션 서버에서 SELinux가 필수가 아닌 환경이라면 비활성화가 더 단순하고 확실한 해결책입니다.

결과 확인 — 인증서 재발급

SELinux 비활성화 후 certbot을 재실행하면 인증서가 정상 발급됩니다. 인증서 경로와 만료일을 확인합니다.

@:~# sudo certbot certonly --cert-name  -d www.
...
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live//fullchain.pem
Key is saved at:         /etc/letsencrypt/live//privkey.pem
This certificate expires on 2025-03-31.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

자동 갱신 확인

certbot은 인증서 발급 완료 시 --standalone 또는 --webroot 플러그인에 따라 systemd timer 또는 cron 기반 자동 갱신 태스크를 등록합니다. 자동 갱신이 정상 동작하려면 서버가 재부팅된 후에도 SELinux 상태가 disabled인지 확인해야 합니다. SELinux를 재활성화한 경우 certbot 갱신 시 다시 차단될 수 있습니다.

# systemd timer 확인 (RHEL 8+)
systemctl status certbot-renew.timer

# 갱신 dry-run 테스트
certbot renew --dry-run

# 인증서 만료일 확인
certbot certificates

참고

관련 포스트:

참고 문서: certbot standalone 플러그인 공식 문서 · RHEL SELinux 상태 변경 공식 문서

Rocky Linux 9.5 NIC IP Setting

개요

RHEL / CentOS 계열 OS에서는 오랫동안 /etc/sysconfig/network-scripts/ifcfg-* 파일로 Rocky Linux NIC IP 설정을 관리했습니다. 그러나 Rocky Linux 9(RHEL 9 계열)부터는 해당 방식이 공식적으로 지원 중단되었으며, NetworkManager가 관리하는 /etc/NetworkManager/system-connections/*.nmconnection 파일 방식으로 전환되었습니다. 이 글에서는 Rocky Linux 9.5 환경에서 NMConnection 파일을 수정하고 nmcli로 변경사항을 반영하는 방법을 정리합니다.

기존 방식 지원 중단 확인

Rocky Linux 9에서 /etc/sysconfig/network-scripts/ 디렉토리를 확인하면 ifcfg 파일 대신 안내 문서만 남아 있습니다. 더 이상 이 디렉토리에서 네트워크 설정이 동작하지 않습니다.

[root@localhost ~]# ll /etc/sysconfig/network-scripts/
total 4
-rw-r--r--. 1 root root 1244 Nov  7 13:30 readme-ifcfg-rh.txt

NetworkManager와 ifcfg의 차이

RHEL/CentOS 7까지는 /etc/sysconfig/network-scripts/ifcfg-* 파일을 직접 편집하고 systemctl restart network로 적용하는 방식이 표준이었습니다. 그러나 RHEL 8부터 network 서비스가 deprecated되고 NetworkManager가 유일한 네트워크 관리 백엔드로 자리잡았습니다. RHEL 9/Rocky Linux 9에서는 ifcfg 파일 자체가 더 이상 지원되지 않습니다.

NetworkManager는 /etc/NetworkManager/system-connections/*.nmconnection 형식으로 연결 프로파일을 저장합니다. 파일 권한은 600이어야 하며, 다른 권한이면 NetworkManager가 해당 파일을 무시합니다. 파일 편집 후에는 반드시 nmcli connection reload로 변경사항을 NetworkManager에 반영해야 합니다.

NMConnection 파일로 수동 IP 설정

NetworkManager가 관리하는 연결 설정 파일은 /etc/NetworkManager/system-connections/ 하위에 있으며, NIC 이름(예: ens33)에 해당하는 .nmconnection 파일을 직접 편집합니다. [ipv4] 섹션의 methodauto에서 manual로 변경하고, address1에 IP/CIDR,게이트웨이 형식으로 주소를 입력합니다. 편집 후에는 nmcli connection reload로 설정을 반영하고 nmcli connection up <NIC명>으로 활성화합니다.

[root@localhost ~]# ll /etc/NetworkManager/system-connections/
total 4
-rw-------. 1 root root 227 Dec 30 17:19 ens33.nmconnection

[root@localhost ~]# vi /etc/NetworkManager/system-connections/ens33.nmconnection

[ipv4]
method=manual
address1=x.x.x.x/24,x.x.x.x
dns=8.8.8.8

[root@localhost ~]# nmcli connection reload
[root@localhost ~]# nmcli connection up ens33
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/3)

nmcli 명령어로 직접 IP 설정하는 방법

파일을 직접 편집하는 대신 nmcli 명령어로 IP를 설정할 수도 있습니다. 명령어 방식은 파일 편집 실수를 줄일 수 있고, 스크립트에서 자동화하기 용이합니다.

# 기존 연결의 IP 설정을 수동(manual)으로 변경
nmcli connection modify ens33 ipv4.method manual
nmcli connection modify ens33 ipv4.addresses "x.x.x.x/24"
nmcli connection modify ens33 ipv4.gateway "x.x.x.x"
nmcli connection modify ens33 ipv4.dns "8.8.8.8"

# 설정 활성화
nmcli connection up ens33

nmcli connection modify는 내부적으로 해당 .nmconnection 파일을 수정합니다. DHCP로 전환하려면 ipv4.method auto로 변경하고 ipv4.addresses, ipv4.gateway를 비워주면 됩니다. 여러 DNS 서버를 지정할 때는 ipv4.dns "8.8.8.8 8.8.4.4"처럼 공백으로 구분합니다.

설정 결과 확인

nmcli device show <NIC명>으로 IP 주소, 게이트웨이, DNS가 올바르게 반영되었는지 확인합니다. 이후 ping을 통해 외부 통신이 가능한지 검증합니다.

[root@localhost ~]$ nmcli device show ens33
GENERAL.DEVICE:                         ens33
GENERAL.TYPE:                           ethernet
GENERAL.CONNECTION:                     ens33
WIRED-PROPERTIES.CARRIER:               on
IP4.ADDRESS[1]:                         x.x.x.x/24
IP4.GATEWAY:                            x.x.x.x
IP4.ROUTE[1]:                           dst = x.x.x.x/24, nh = 0.0.0.0, mt = 100
IP4.ROUTE[2]:                           dst = 0.0.0.0/0, nh = x.x.x.x, mt = 100
IP4.DNS[1]:                             8.8.8.8

참고

관련 포스트:

참고 문서: RHEL 9 Ethernet 연결 설정 공식 문서 · NetworkManager nmcli 설정 레퍼런스