infra
Platform

모듈 맵

[Infra Ops] WAR 배포부터 server.xml 튜닝, 장애 대응까지

0 / 52 완료

펼치기
0 / 52 완료0%

Infra-ops · 16 / 52

[Infra Ops] WAR 배포부터 server.xml 튜닝, 장애 대응까지

WAR 배포, server.xml/context.xml 설정, JNDI DataSource, maxThreads 튜닝, catalina.out 분석까지 — 현장 인프라 엔지니어의 Tomcat 운영 실무

🚨INCIDENT ALERT
HIGH

운영팀에서 급하게 연락이 왔습니다. 고객사 배포 요청이 들어와 myapp.war 파일을 /opt/tomcat/webapps/에 복사했는데 5분이 지나도 서비스가 뜨질 않습니다. 브라우저에서 8080 포트로 접근하면 Tomcat 기본 화면이 나오거나 404가 뜹니다. 로그를 봐야 한다는 건 알겠는데 로그 파일이 어디 있는지, 어떤 줄을 봐야 하는지 막막합니다.

WAR가 자동으로 풀리는 원리, catalina.out에서 에러를 찾는 법, 그리고 배포가 실패했을 때 롤백하는 루틴 — 이 모든 것이 이번 모듈의 주제입니다. Tomcat을 "그냥 WAR 올리는 곳"이 아니라 이해하고 운영하는 것을 목표로 합니다.

이번 챕터에서 배울 것
  • 1Tomcat 디렉터리 구조와 각 폴더의 역할을 설명할 수 있다
  • 2server.xml의 Connector 설정(maxThreads, acceptCount, connectionTimeout)을 읽고 튜닝할 수 있다
  • 3context.xml에 JNDI DataSource를 설정하고 연결 풀 파라미터를 조정할 수 있다
  • 4WAR 배포 절차와 배포 실패 시 catalina.out에서 원인을 찾을 수 있다
  • 5catalina.out에서 OutOfMemoryError, 클래스 충돌, 한국어 인코딩 문제를 진단할 수 있다

Tomcat 디렉터리 구조

💡개념

$CATALINA_HOME 구조와 각 디렉터리의 역할

Tomcat을 제대로 운영하려면 어떤 파일이 어디에 있는지를 알아야 합니다. 로그를 찾지 못해 장애 대응이 늦어지거나, 라이브러리를 잘못된 경로에 두어 클래스를 못 찾는 문제가 현장에서 반복됩니다. 디렉터리 구조는 한 번 제대로 익혀두면 어떤 버전의 Tomcat이든 동일하게 적용됩니다.

Tomcat 디렉터리 구조 다이어그램

로컬 터미널
$CATALINA_HOME/
├── bin/          # Tomcat 실행·종료 스크립트
│   ├── startup.sh      # start → catalina.sh start 호출
│   ├── shutdown.sh     # stop → catalina.sh stop 호출
│   └── catalina.sh     # 실제 JVM 기동 파라미터 담당 (JAVA_OPTS 등)
├── conf/         # 핵심 설정 파일 모음
│   ├── server.xml      # Connector, Host, Engine 등 서버 전체 구조
│   ├── context.xml     # 전역 컨텍스트 설정 (JNDI DataSource 등)
│   ├── web.xml         # 서블릿 기본 동작 (DefaultServlet, JSP 엔진 등)
│   └── tomcat-users.xml # Manager App 접근 계정 (운영 환경 주의)
├── logs/         # 로그 파일 — 장애 대응 시 가장 먼저 열 곳
│   ├── catalina.out        # 표준 출력+에러, 주 진단 대상
│   ├── localhost.YYYY-MM-DD.log  # 앱별 로그
│   └── localhost_access_log.YYYY-MM-DD.txt  # HTTP 접근 로그
├── webapps/      # WAR 배포 위치 — Tomcat이 감시하는 폴더
│   ├── ROOT/           # / 경로로 접근되는 기본 앱
│   └── myapp/          # myapp.war 압축 해제 결과
├── work/         # JSP를 컴파일한 .java/.class 파일 캐시
│   └── Catalina/localhost/myapp/  # 앱별 컴파일 캐시
└── lib/          # Tomcat + 모든 앱이 공유하는 JAR
    └── mysql-connector-j-8.x.jar  # JDBC 드라이버는 여기 배치

CATALINA_HOME vs CATALINA_BASE — 멀티 인스턴스 운영:

변수의미공유 여부
CATALINA_HOMETomcat 바이너리 설치 경로 (bin/, lib/)여러 인스턴스가 공유
CATALINA_BASE인스턴스별 설정·배포·로그 경로인스턴스마다 독립

서버에 Tomcat 인스턴스를 2개 이상 운영할 때(8080, 8090 포트 등) CATALINA_HOME은 하나로 두고, CATALINA_BASE를 인스턴스마다 다르게 지정합니다. /opt/tomcat-8080/, /opt/tomcat-8090/ 식으로 분리하면 로그와 설정이 섞이지 않습니다.

로컬 터미널
# 실행 중인 Tomcat 프로세스와 CATALINA_HOME 확인
ps aux | grep catalina | grep -v grep
# 출력 예시:
# tomcat   1234  2.1 12.3 3456789 234567 ?  Sl  09:00  0:45
#   /usr/lib/jvm/java-17/bin/java ... -Dcatalina.home=/opt/tomcat ...

server.xml 핵심 설정

💡개념

Connector 설정 — 스레드와 연결을 제어하는 핵심 파라미터

server.xml은 Tomcat 전체 구조를 정의합니다. 그 중 Connector는 외부 HTTP 요청을 받는 입구입니다. 여기서 스레드 수, 대기 큐, 타임아웃을 잘못 설정하면 트래픽 급증 시 서비스가 멈추거나, 반대로 리소스를 낭비합니다.

XML
<!-- /opt/tomcat/conf/server.xml — Connector 핵심 설정 -->
<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           maxThreads="200"
           minSpareThreads="10"
           acceptCount="100"
           redirectPort="8443"
           URIEncoding="UTF-8" />

파라미터별 역할과 튜닝 기준:

파라미터기본값의미튜닝 기준
maxThreads200동시에 처리할 수 있는 요청 스레드 최대 수CPU 코어 수 × 2~4에서 시작, 부하 테스트로 결정
minSpareThreads10항상 대기 중인 최소 스레드 수트래픽 패턴에 따라 25~50 사이
acceptCount100maxThreads 소진 시 OS 큐에서 대기할 요청 수너무 크면 클라이언트 타임아웃 전에 대기, 너무 작으면 연결 거부
connectionTimeout20000(ms)클라이언트가 연결 후 요청을 보내지 않을 때 대기 시간Keep-alive 환경에서는 줄이는 경우 있음
URIEncodingISO-8859-1URL 경로의 문자 인코딩한국어 서비스라면 UTF-8 필수

요청 처리 흐름 — 스레드가 소진되면 어떻게 되나:

  1. 요청이 들어오면 가용 스레드가 할당되어 처리
  2. 가용 스레드가 maxThreads에 도달하면 → acceptCount 크기의 OS 큐에 대기
  3. OS 큐도 가득 차면 → 새 연결은 즉시 거부(Connection refused)

따라서 maxThreads를 무작정 높이는 것이 해결책이 아닙니다. 스레드가 오래 점유되는 근본 원인(느린 DB 쿼리, 외부 API 지연)을 먼저 찾아야 합니다.

로컬 터미널
# server.xml 설정 확인 (grep으로 빠르게)
grep -A 5 'Connector port="8080"' /opt/tomcat/conf/server.xml

Tomcat Thread Pool — 스레드 상태와 튜닝 기준

JNDI DataSource 설정

💡개념

context.xml에서 DB 연결 풀 설정하기 — 실무 핵심

운영 중인 서비스가 갑자기 "DB 연결 실패" 에러를 쏟아내기 시작했습니다. 로그를 보니 Cannot get a connection, pool error Timeout waiting for connection from pool입니다. DB 자체는 살아있는데 연결을 못 받는 상황 — 연결 풀이 고갈된 것입니다. 처음 배포할 때 기본값 그대로 뒀다가 트래픽이 늘면서 터지는 전형적인 패턴입니다. context.xml 설정 한 줄 차이가 서비스 가용성을 결정합니다.

애플리케이션이 DB에 접근하는 방법은 두 가지입니다. 코드 내에서 직접 JDBC 연결을 생성하거나, Tomcat이 관리하는 JNDI DataSource를 통해 연결 풀을 빌려 쓰는 방식입니다. 실무에서는 거의 항상 JNDI DataSource를 씁니다 — DB 연결 생성 비용을 줄이고, 연결 수를 제어하며, 코드와 DB 설정을 분리할 수 있기 때문입니다.

XML
<!-- /opt/tomcat/conf/context.xml — 전역 DataSource 설정 -->
<Context>
  <Resource name="jdbc/myapp"
            auth="Container"
            type="javax.sql.DataSource"
            driverClassName="com.mysql.cj.jdbc.Driver"
            url="jdbc:mysql://db-server:3306/mydb?useSSL=false&amp;serverTimezone=Asia/Seoul"
            username="appuser"
            password="secret"
            maxTotal="30"
            maxIdle="10"
            minIdle="5"
            maxWaitMillis="10000"
            validationQuery="SELECT 1"
            testOnBorrow="true"
            timeBetweenEvictionRunsMillis="60000"
            minEvictableIdleTimeMillis="300000" />
</Context>

연결 풀 파라미터 튜닝 기준:

파라미터의미실무 가이드
maxTotal풀에서 생성할 수 있는 최대 연결 수DB 서버의 max_connections와 맞춰서 설정. 보통 20~50
maxIdle유휴 상태로 유지할 최대 연결 수maxTotal의 30~50%
minIdle항상 유지할 최소 유휴 연결 수트래픽이 갑자기 올 때 연결 대기 없이 바로 처리
maxWaitMillis풀에서 연결을 못 받을 때 대기할 최대 시간(ms)10000(10초)이 일반적. 초과 시 SQLException
validationQuery연결을 빌리기 전 DB에 보내는 확인 쿼리SELECT 1 (MySQL), SELECT 1 FROM DUAL (Oracle)
testOnBorrow연결 대출 시마다 validationQuery 실행 여부true 권장 — 끊어진 연결을 앱에 전달하지 않음

JDBC 드라이버 배치 위치:

드라이버 JAR(mysql-connector-j-8.x.jar 등)는 반드시 $CATALINA_HOME/lib/에 둬야 합니다. webapps/앱명/WEB-INF/lib/에 드라이버를 두면 Tomcat 레벨의 JNDI DataSource가 드라이버를 찾지 못해 ClassNotFoundException이 발생합니다.

로컬 터미널
# JDBC 드라이버 배치 및 확인
sudo cp mysql-connector-j-8.x.jar /opt/tomcat/lib/
ls -la /opt/tomcat/lib/ | grep mysql
# Tomcat 재시작 후 반영됨 (lib 변경은 재시작 필요)

WAR 배포와 운영 루틴

💡개념

WAR 배포 방식 — 자동 배포와 수동 배포의 차이

밤 11시에 긴급 배포를 해야 합니다. WAR 파일을 webapps/ 에 복사했는데 15분째 서비스가 뜨질 않습니다. 로그를 보러 갔더니 catalina.out이 수백 MB — 어디서부터 봐야 할지 모릅니다. 설상가상으로 롤백해야 하는데 이전 WAR 파일을 어디 뒀는지도 불확실합니다. 배포 방식을 이해하지 못한 채 운영에 들어가면 이런 상황이 반복됩니다.

Tomcat에 애플리케이션을 배포하는 방법은 상황에 따라 다릅니다. 개발 환경과 운영 환경에서 다른 방식을 써야 하고, 각 방식의 동작 원리를 이해해야 배포가 왜 실패했는지를 알 수 있습니다.

배포 방식 비교:

방식절차적합한 환경주의사항
autoDeploy (자동)WAR를 webapps/에 복사 → Tomcat이 감지·배포개발, 테스트운영에서는 autoDeploy=false 권장
수동 배포Tomcat 중지 → WAR 복사 → Tomcat 시작운영 (중단 허용 시)서비스 다운타임 발생
Manager App웹 UI에서 undeploy → deploy운영 (간헐적 변경)tomcat-users.xml 계정 관리 필요

autoDeploy 설정 위치 (server.xml):

XML
<!-- server.xml의 Host 엘리먼트 -->
<Host name="localhost" appBase="webapps"
      unpackWARs="true"
      autoDeploy="true">   <!-- 운영 환경에서는 false 권장 -->

배포 후 성공 확인 기준:

로컬 터미널
# catalina.out에서 정상 기동 확인
grep "Server startup in" /opt/tomcat/logs/catalina.out | tail -3
# 정상 출력 예시:
# INFO [main] org.apache.catalina.startup.Catalina.start
#   Server startup in [3456] milliseconds

기동 완료 메시지가 나오기 전까지는 서비스가 준비되지 않은 것입니다. 로드밸런서 헬스체크가 이 시간보다 짧으면 503이 발생할 수 있습니다.

1WAR 배포 및 기동 확인

WAR 파일을 webapps/에 복사합니다. autoDeploy=true(기본값)이면 Tomcat이 자동으로 감지해 배포를 시작합니다. 복사 직후부터 배포가 완료되기까지 catalina.out을 실시간으로 확인해야 합니다.

로컬 터미널
# WAR 복사 후 압축 해제 상태 확인 (디렉터리가 생겨야 정상)
ls -la /opt/tomcat/webapps/myapp/

# 배포 로그 실시간 모니터링 (INFO, ERROR, WARN 라인만 필터)
tail -f /opt/tomcat/logs/catalina.out | grep -E "INFO|ERROR|WARN|SEVERE"

# 배포 완료 확인 (Server startup 메시지)
grep "Server startup in" /opt/tomcat/logs/catalina.out | tail -3

# 애플리케이션 헬스체크
curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/myapp/health

배포 후 webapps/myapp/ 디렉터리가 생성되고 catalina.out에 "Server startup in X ms" 가 출력되면 정상 배포입니다.

sudo cp myapp.war /opt/tomcat/webapps/
🔍실행 후 확인할 것
  • ls /opt/tomcat/webapps/myapp/ — WAR 압축 해제 결과인 WEB-INF/ 디렉터리가 존재하는가
  • catalina.out에 'SEVERE' 또는 'Exception' 라인 없이 'Server startup in' 메시지가 나왔는가
  • curl http://localhost:8080/myapp/health 에서 HTTP 200이 반환됐는가
  • catalina.out에서 배포 시작 시각과 완료까지 걸린 시간(ms)을 확인했는가
2스레드·연결 상태 확인 및 슬로우 요청 탐지

배포 후 정상 동작하더라도 스레드 점유와 응답 시간을 주기적으로 확인해야 합니다. Manager App 상태 페이지와 액세스 로그를 결합해 병목 요청을 찾습니다.

로컬 또는 서버
# Manager App XML 상태에서 현재 스레드 수 확인
# (tomcat-users.xml에 manager-status 롤 계정 필요)
curl -s -u admin:adminpass \
  http://localhost:8080/manager/status?XML=true \
  | grep -i "maxThreads\|currentThreadCount\|currentThreadsBusy"

# 액세스 로그에서 응답시간 5000ms 초과 요청 찾기
# 액세스 로그 마지막 필드가 응답시간(ms)인 경우
awk '$NF > 5000 {print}' /opt/tomcat/logs/localhost_access_log.*.txt | tail -20

# catalina.out에서 에러 라인 추출
grep -E "Exception|Error|SEVERE" /opt/tomcat/logs/catalina.out | tail -30

# OutOfMemoryError 발생 여부 확인
grep "java.lang.OutOfMemoryError" /opt/tomcat/logs/catalina.out

액세스 로그 형식은 server.xmlValve 설정에 따라 다릅니다. %D 또는 %T 포맷이 응답시간을 기록하도록 설정되어 있어야 합니다.

grep -E 'Exception|Error|SEVERE' /opt/tomcat/logs/catalina.out | tail -30
🔍실행 후 확인할 것
  • Manager status 페이지에서 currentThreadsBusy가 maxThreads의 70% 이하인가
  • 액세스 로그에서 5초 이상 응답 요청이 반복 패턴을 보이는 특정 URI가 있는가
  • catalina.out grep 결과에 'java.lang.OutOfMemoryError' 또는 'ClassNotFoundException'이 없는가
  • 에러 라인이 있다면 동일한 Exception 클래스가 반복되는지 패턴을 확인했는가

트러블슈팅

원인: JNDI DataSource 연결 풀의 모든 연결(maxTotal)이 소진되어 maxWaitMillis 시간 동안 대기했지만 연결을 받지 못한 상황입니다. 두 가지 원인이 가장 흔합니다.

  • DB 슬로우 쿼리: 쿼리가 오래 걸려 스레드가 DB 연결을 오래 잡고 있음
  • 연결 누수(Leak): 코드에서 Connection.close()를 호출하지 않아 연결이 반환되지 않음
로컬 터미널
# 1. catalina.out에서 에러 발생 시각 확인
grep "Cannot get a connection" /opt/tomcat/logs/catalina.out | tail -10

# 2. 에러 발생 시각의 DB 슬로우 쿼리 로그 대조 (MySQL 예시)
# /var/log/mysql/slow.log 또는 MySQL slow query log 설정 필요

# 3. 현재 DB 연결 상태 확인 (MySQL)
# MySQL 접속 후:
# SHOW STATUS LIKE 'Threads_connected';
# SHOW PROCESSLIST;

# 4. context.xml에서 연결 풀 설정 확인
grep -A 15 'Resource name="jdbc' /opt/tomcat/conf/context.xml

# 5. 연결 누수 감지 설정 추가 (context.xml)
# removeAbandonedOnBorrow="true"
# removeAbandonedTimeout="60"
# logAbandoned="true"

단기 조치: maxTotal 값을 임시로 늘려 여유를 확보하고, 동시에 슬로우 쿼리 분석을 병행합니다. maxTotal을 무한정 늘리면 DB 서버의 max_connections를 초과해 DB 자체가 다운될 수 있으므로 주의합니다.

원인: WAR 내부에 클래스 충돌이나 의존성 문제가 있을 때 배포 자체가 실패합니다. 가장 흔한 케이스는 WEB-INF/lib/에 포함된 JAR와 $CATALINA_HOME/lib/의 JAR 버전 충돌입니다.

로컬 터미널
# 1. catalina.out에서 상세 스택 트레이스 확인
grep -A 30 "SEVERE: Error deploying" /opt/tomcat/logs/catalina.out | tail -40

# 2. 스택 트레이스에서 충돌 클래스 확인
# 예시: java.lang.NoSuchMethodError, java.lang.ClassNotFoundException
# 예시: org.springframework.beans.factory.BeanDefinitionStoreException

# 3. WAR 내부 lib 목록 확인
jar -tf /opt/tomcat/webapps/myapp.war | grep "WEB-INF/lib/" | sort

# 4. CATALINA_HOME/lib과 중복 여부 비교
ls /opt/tomcat/lib/ | sort

# 5. 충돌 JAR 제거 후 WAR 재빌드 (개발팀 협조)
# 또는 이전 배포 버전으로 롤백
sudo cp /backup/myapp-previous.war /opt/tomcat/webapps/myapp.war

# 6. work 디렉터리 캐시 삭제 (클래스 캐시 충돌 해소)
sudo systemctl stop tomcat
sudo rm -rf /opt/tomcat/work/Catalina/localhost/myapp/
sudo systemctl start tomcat

배포 실패 시 webapps/myapp/ 디렉터리가 생성되다 멈추거나 불완전하게 남을 수 있습니다. work/ 캐시까지 같이 지우는 것이 깔끔합니다.

원인: server.xml의 Connector에 URIEncoding="UTF-8"이 설정되어 있지 않으면, URL 경로에 포함된 한국어(URL 인코딩된 멀티바이트 문자)가 ISO-8859-1로 디코딩되어 깨집니다. POST 바디는 애플리케이션에서 설정하지만, URI 경로는 Tomcat Connector 레벨에서 처리합니다.

로컬 터미널
# 1. 현재 Connector 설정 확인
grep -i "URIEncoding\|encoding" /opt/tomcat/conf/server.xml

# 2. server.xml Connector에 URIEncoding 추가
# (변경 후 Tomcat 재시작 필요)
# <Connector port="8080" ... URIEncoding="UTF-8" />

# 3. 변경 적용을 위한 재시작
sudo systemctl restart tomcat

# 4. 인코딩 확인 테스트 (한국어 URL 파라미터)
curl -v "http://localhost:8080/myapp/search?keyword=%ED%85%8C%EC%8A%A4%ED%8A%B8"
# 애플리케이션에서 "테스트"로 제대로 받는지 확인

# 5. Tomcat 9 이상에서는 기본값이 UTF-8이므로
#    버전 확인도 중요
/opt/tomcat/bin/version.sh | grep "Server version"

Tomcat 9.0부터는 URIEncoding 기본값이 UTF-8로 변경되었습니다. Tomcat 8.x 이하를 사용 중이라면 반드시 명시적으로 설정해야 합니다.

실무 운영 루틴

💼
실무 맥락
현업 패턴

금융사·공공기관 Tomcat 운영에서 반복되는 패턴:

1. 배포 전 체크리스트

운영 환경에서 WAR 배포 전 반드시 확인해야 하는 항목입니다.

로컬 터미널
# 현재 운영 중인 WAR 백업
sudo cp /opt/tomcat/webapps/myapp.war \
  /backup/myapp-$(date +%Y%m%d-%H%M%S).war

# 배포 전 Tomcat 상태 확인
sudo systemctl status tomcat

# 배포 전 JVM 힙 사용량 확인 (jstat 사용)
jstat -gc $(pgrep -f catalina) 5000 3

2. 무중단 배포 (인스턴스 2대 이상 운영 시)

로드밸런서(Nginx, L4) 뒤에 Tomcat 인스턴스가 2대 있을 때의 표준 절차입니다.

서버 터미널
# 1단계: 첫 번째 인스턴스를 로드밸런서에서 제외 (Nginx 설정에서 weight=0 또는 down)
# 2단계: 첫 번째 인스턴스에 새 WAR 배포
sudo systemctl stop tomcat-8080
sudo cp new-myapp.war /opt/tomcat-8080/webapps/myapp.war
sudo systemctl start tomcat-8080

# 3단계: 헬스체크로 정상 확인
curl -s http://localhost:8080/myapp/health

# 4단계: 로드밸런서에 첫 번째 인스턴스 복귀
# 5단계: 두 번째 인스턴스 동일 절차 반복

3. JNDI 패스워드 암호화 (보안 요구사항)

공공기관·금융권에서는 context.xml의 DB 패스워드를 평문으로 두면 보안 감사에서 지적됩니다.

로컬 터미널
# Jasypt 또는 Tomcat 자체 암호화 도구 사용
# context.xml에서 password 대신 암호화된 값 + 복호화 DataSource 구현 필요
# 보안 담당자와 사전 협의 필요

# 최소한: context.xml 파일 권한 제한
sudo chmod 600 /opt/tomcat/conf/context.xml
sudo chown tomcat:tomcat /opt/tomcat/conf/context.xml

4. 장애 대응 첫 3분 루틴

로컬 터미널
# 1. Tomcat 프로세스 존재 여부
pgrep -a -f catalina

# 2. 포트 리스닝 확인
ss -tlnp | grep 8080

# 3. 최근 에러 로그 빠른 확인
grep -E "SEVERE|OutOfMemoryError|StackOverflowError" \
  /opt/tomcat/logs/catalina.out | tail -20

# 4. 헬스체크 응답코드 확인
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/myapp/health

# 5. JVM 스레드 덤프 (응답은 있지만 느릴 때)
kill -3 $(pgrep -f catalina)
# 스레드 덤프는 catalina.out에 출력됨

Tomcat은 여전히 국내 금융·공공 환경에서 가장 많이 쓰이는 WAS입니다. server.xmlcontext.xml을 자신 있게 다루고, catalina.out에서 원인을 찾는 루틴을 익혀두면 배포·장애 대응 속도가 확실히 달라집니다. 다음 모듈에서는 Tomcat 세션 클러스터링과 Redis 세션 외부화를 다룹니다.

지식 확인

퀴즈 — 4문제

Q1

WAR 배포 후 Tomcat이 webapps 하위에 자동으로 압축 해제하도록 제어하는 server.xml Host 속성은?

Q2

JNDI DataSource를 server.xml이 아닌 context.xml에 정의하는 가장 중요한 이유는?

Q3

Tomcat Connector의 maxThreads 기본값과, 이 값을 지나치게 높게 설정했을 때 발생할 수 있는 주요 문제는?

Q4

Tomcat catalina.out 파일에서 OutOfMemoryError 발생 라인을 빠르게 찾는 grep 명령어는?

0 / 4 답변

🧪 실습으로 확인하기

Nginx 설치 및 기동

초급

Linux 서버에 Nginx를 설치하고 systemd 서비스로 등록하여 80포트에서 응답하는 상태까지 만든다.

30📋 3단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

infra-ops중급 · 65
[Infra Ops] Heap/GC/Thread Dump 분석과 OOM 대응 실무
인프라 서비스 운영 트랙 계속
linux입문 · 30
[Linux] 개발자가 왜 리눅스 서버와 커맨드라인을 반드시 배워야 하는가
Linux 트랙 시작점