설치 및 구동

RHEL/CentOS 설치 옵션에 따라 다르지만 서버로 설치했을 경우 아파치 웹서버는 기본적으로 설치가 된다. 혹시 설치가 안 됐을 경우 yum 으로 간단하게 설치할 수 있다.

yum install httpd

설치후 /etc/httpd 에 설정과 로그 파일이 생기게 된다. 하위 디렉터리별 용도를 살펴 보자.

  • conf : 웹서버의 주요 설정 파일인 httpd.conf 파일이 있는 곳이다. 그외 적절한 MIME 형식을 지정하기 위한 파일들의 포맷이 지정되어 있는 파일인 magic 파일이 있다.
  • conf.d : 아파치의 모듈 설정이 있는 디렉터리이다. php 나 subversion, mod_ssl  등 아파치 모듈을 설치하면 모듈별 설정 파일이 같이 설치된다. 설정 파일명은 모듈명.conf 이다. php 모듈 설치시 php.conf 파일이 같이 설치된다. httpd.conf 에서 다음과 같이 conf.d 에 있는 파일을 로딩하게 설정되어 있다.

    Include conf.d/*.conf

  • logs : 로그 파일이 남는 디렉터리로 /var/log/httpd 에 대한 심볼릭 링크이다.
  • modules : 아파치 모듈이 설치되는 디렉터리로 /usr/lib64/httpd/modules/ 에 대한 심볼릭 링크이다. 모듈들의 SELinux 컨텍스트는 httpd_modules_t 이다.
  • run : service 명령에서 구동 여부를 확인할 수 있도록 pid 정보등 실행 정보를 기록하는 디렉터리로 /var/run/httpd 에 대한 심볼릭 링크이다. 


설치가 끝났으면 외부에서 연결 가능하게 방화벽을 열어야 한다. lokkit 명령어로 iptables 설정을 수정하자.

lokkit -s http

lokkit 는 기본적으로 iptables 를 재구동하므로 service iptables restart 명령어를 사용하지 않아도 된다.


보통 웹서버는 부팅과 동시에 서비스로 구동되어야 한다. 전절에서 학습한 chkconfig 명령어로 부팅과 동시에 웹서버가 구동되게 설정해 보자.

chkconfig httpd on

chkconfig httpd --list

httpd           0:해제  1:해제  2:활성  3:활성  4:활성  5:활성  6:해제


설치가 끝났으면 아파치 웹서버를 시작하고 종료하는 명령어에 대해서 알아 보자. RHEL 에서는 아파치 웹서버도 다른 서비스처럼 service 명령어를 사용하여 시작/종료하면 된다. 시작/종료는 다른 데몬처럼 service 명령어 뒤에 start, stop 을 기술하면 되므로 생략하고 아파치 웹서버만 제공하는 별도의 옵션에 대해서 알아 보자.


graceful

service httpd graceful 명령어로 사용하며 재시작시 사용할 수 있다. 이미 httpd 가 떠 있을 경우 기존 프로세스를 바로 종료하지 않고 자식 프로세스에게 현재 요청을 모두 처리한 후에 종료하도록 지시한다.  


configtest

httpd.conf 파일의 설정 이상 유무를 확인할 수 있는 옵션이다. 설정 파일을 변경했으면 바로 웹서버를 재구동하지 말고 이 명령어를 실행해서 설정 파일이 이상 없는지 확인후 재구동하는 게 좋다.

service httpd configtest 명령어로 사용할 수 있다. 


httpd.conf 설정

httpd.conf 는 웹서버의 주요 설정 파일로 웹서버의 동작을 세밀하게 지정할 수 있다. 

CentOS 에 포함된 아파치 웹서버는 설정이 전역 설정, 서버 설정, 가상 호스트 설정으로 크게 세 가지 항목으로 나눠져 있다.

각각의 항목중에 중요한 부분만 알아 보자.

전역 설정


ServerRoot

아파치 웹 서버의 설정 파일, 로그 파일이 있는 최상위 디렉터리이다. 기본 설정은 "/etc/httpd" 이며 특별한 이유가 있지 않은 이상 이 값을 수정할 필요가 없다.


ServerTokens

에러가 발생하거나 페이지를 못 찾는 경우 같은 경우 아파치 웹서버의 정보도 같이 출력하게 된다. 기본 옵션이 OS 이며 다음과 같이 웹서버의 버전과 OS 의 종류도 출력하게 된다.

Apache/2.2.15 (CentOS) Server at www.example.com Port 80

이것은 너무 많은 정보이므로 다음과 같이 웹서버의 종류만 출력하게 Prod 로 설정하는 게 좋다.

Apache Server at www.example.com Port 80


KeepAlive

KeepAlive 는 HTTP 의 지속적인 연결을 사용하여 하나의 세션에서 여러 개의 요청을 처리하는 것이다.

RHEL/CentOS 의 기본 설정은 Off 이다. KeeyAlive 를 On 으로 설정할 경우 클라이언트는 세션을 유지하여 여러 커넥션에서 동시에 자료를 전송받을 수 있다. 하나의 웹 페이지에서 이미지와 자바 스크립트등 여러 리소스를 제공하는 경우가 많으므로 On 으로 하면 메모리는 많이 먹지만 성능은 상당히 빨라진다.


MaxKeepAliveRequests

한 세션에서 동시에 연결할 수 있는 클라이언트의 갯수이다. 기본값은 100 이며 메모리가 충분하다면 기본값보다 넉넉하게 주는게 좋다.


KeepAliveTimeout

같은 클라이언트에서 처리할 시간을 지정한다. 기본 값은 15초이며 네트워크가 느린 경우 약간 더 크게 주는게 좋다. KeepAliveTimeout을 크게 잡을 경우 처리가 안 된 클라이언트들은 소켓의 갯수를 차지하게 되므로 MaxKeepAliveRequests 도 크게 잡는게 좋다.


Listen

80 이 기본값으로 다른 포트도 사용하려면 Listen 구문을 여러 번 사용하면 된다. 다음 설정은 80, 8080 두 개의 포트를 Listen 하게 된다.

Listen 80
Listen 8080


LoadModule

오브젝트 파일이나 라이브러리를 읽어 들인후에 사용 가능한 모듈 목록에 추가하는 지시자이다. 

동적 공유 모듈(Dynamic Shared Object) 형태로 된 모듈에 대해서 사용해야 하며 CentOS 에 설치되는 아파치는 모든 확장 모듈을 동적 공유 모듈로 패키징해서 제공하고 있다.

많이 사용되는 모듈들은 모두 LoadModule 로 읽어 드리고 있으며 사용 빈도가 적은 모듈들은 주석으로 처리되어 있으므로 혹시 필요한 모듈이 주석 처리 되어 있다면 주석을 해제하고 서버를 재구동하면 된다.


Include

다른 설정 파일을 포함 시키는 지시자이다. 하나의 파일에 모든 설정을 담고 있으면 관리와 수정이 어려우므로 용도별로 설정 파일을 나눈 후에 Include 구문으로 포함시키는 것이 관리가 더 용이하다.

모듈 설정 파일이 있는 conf.d 디렉터리의 모든 설정 파일을 로딩하는 내용이 httpd.conf 에 기본 포함되어 있다.

Include conf.d/*.conf

상대 경로로 사용할 경우 ServerRoot 를 상위 디렉터리로 찾게 된다. 


User, Group

유닉스 계열의 OS 는 1024 이하의 포트를 사용하려면 루트 사용자만 가능하다. 그러므로 아파치 구동은 루트로 하여야 하나 이럴 경우 아파치 프로세스가 루트 권한을 갖게 되므로 보안 문제가 발생할 수 있다.

이때문에 최초 구동은 루트로 하지만 자식 프로세스를 생성한 후에(포크 - fork) setuid, setgit 라는 함수 호출을 통해 User, Group 에 지정된 사용자/그룹으로 전환한다.

CentOS 의 경우 아파치기 전환하는 사용자/그룹 이름은 apache:apache 이며 이 계정은 보안을 위해 로그인이 불가능하게 shell 을 /sbin/nologin 으로 설정하는게 좋다.(CentOS 의 기본 설정이다)


ps 로  httpd 프로세스를 보면 하나의 프로세스만 루트이고 나머지는 apache 계정인 이유가 위 설정때문이다.


서버 설정


ServerAdmin

서버의 관리자 이메일 주소를 설정한다. HTTP 500 에러나 404 에러등이 발생할 경우 여기 설정된 메일 주소가 에러 화면에 표시된다. 단 ServerSignature 항목이 Email 로 설정되어 있어야 한다.


ServerName

서버의  전체 주소 도메인 네임(FQDN - Fully Qualified Domain Name) 을 설정하는 게 좋다. 서버의 DNS 가 할당되지 않았다면 IP 를 설정해야 한다. : 을 구분자로 뒤에 포트를 설정해도 되지만 일반적으로 80을 사용하므로 포트는 생략해도 된다.


AllowOverride

웹 컨텐츠 디렉토리 별로 설정이 달라야 할 경우도 있다. 이럴 때 관리자가 모든 설정을 하기는 힘들드니까 개별 디렉터리마다 다른 설정을 할수 있으면 편리할 것이다. .htaccess 파일은 이 용도로 사용되며 주설정 파일과 문법이 동일하다.

AllowOverride 지시자는 웹 컨텐츠의 디렉터리에 .htaccess 파일이 있을 경우 기존 설정을 덮어 쓸지 여부를 지정한다. All 이나 None 또는 개별 항목을 직접 기술할 수 있으며 여러 개를 설정할 경우 | 를 구분자로 적어 주면 된다. All 일 경우 모든 권한을 .htaccess 에서 덮어 쓸수 있게 되며 None 일 경우 .htaccess 파일을 사용할 수 없다.

.htaccess 를 사용하면 웹 컨텐츠 디렉터리마다 .htaccess 파일의 존재 여부를 확인해야 하므로 성능이 떨어질 수 있으며 일반 사용자가 가상 호스트 서버 설정 권한을 갖게 되는 셈이므로 보안상 취약해질 우려가 있다.

CentOS 의 기본 설정은 None 이다.


DocumentRoot 

서버의 웹 컨텐츠가 있는 루트 디렉터리를 지정한다. 기본 설정은 "/var/www/html" 이다. 가상 호스트에서도 사용할 수 있으므로 가상 호스트마다 다른 웹 컨텐츠 제공이 가능해 진다.


DirectoryIndex

브라우저가 웹 서버에 요청시 리소스 명이 디렉토리이고 파일명이 없을 때 기본적으로 읽어서 전송할 파일의 이름이다. 기본 값은 index.html, index.html.var 이며 php 모듈을 설치하면 index.php 가 추가된다.


<Directory> 지시자

디렉토리 단위별 설정을 하는 지시자이다. <Directory dir-path> ... </Directory> 의 형식으로 사용하며 dir-path 부분에 적용할 디렉토리 경로를 설정한다.  <Location> ~</Location>지시자와 유사하며 차이점은 Location 은 URL 을 대상으로 하며 Directory 는 로컬 파일의 경로를 대상으로 한다는 점이다.

Options와 접근 권한을 설정하는 경우가 많다.

Order 키워드는 기본적으로 접근을 허용할지 막을지에 대한 우선 순위를 지정하며 그후에 지정된 순서로 접근 권한을 확인하게 된다. 아래의 설정은 Order 가 allow, deny 이므로 Allow 부분을 먼저 보게 되며 모든 클라이언트로 부터 연결을 허용하게 된다.

<Directory "/var/www/html">
    Options Indexes FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
</Directory>


아파치 웹서버에는 웹서버의 상태를 웹 기반으로 모니터링할 수 있는 mod_status 라는 모듈이 포함되어 있다. 이 모듈은 서버이름/server-status 형식의 URL 로 호출할 수 있다.

기본적으로 주석 처리 되어 있지만 주석을 해제하고 다음처럼 설정해 보자.

URL 이므로 Directory 대신 Location 지시자를 사용하며 누구나 연결 가능하면 안 되므로 Order 를 deny, allow 로 설정하고 Deny from all 을 추가하면 모든 곳에서 연결이 불가하다.

그 후에 Allow 지시자로 연결이 가능한 클라이언트를 적어 주면 된다. 여러 개를 적을 경우 공백을 구분자로 하며 대역폭을 적어줄 수도 있다.

아래 설정은 localhost 와 192.168.152 대역의 IP 에서만 연결이 가능하다.

<Location /server-status>

SetHandler server-status
Order deny,allow
Deny from all
Allow from 127.0.0.1 192.168.152.0/24

</Location>


<Files> ~ </Files

파일 이름에 따라 특별한 처리가 필요할 경우 사용하는 지시자이다. <Files filename> ... </Files> 의 형식으로 사용하며 filename 항목에 적용시킬 파일명을 적어주면 된다.

다음은 cat.html 에 파일에 대해 모두가 접속 가능하게 처리하는 예제이다.

<Files "cat.html">

Order allow, deny
Allow From All

</Files>

파일명에 ?, * 는 특별한 의미로 사용할 수 있다. ? 는 임의의 문자 한 개와 일치하며 * 는 임의의 문자열과 일치한다.

예로 <Files "?at.*"> 는  cat.html, bat.jpg, hat.php 와 일치하게 된다.

 파일명 앞에 ~ 문자를 적어주면 정규식을 사용할 수 있다. 아파치 웹서버는 PCRE (Perl Compatible Regular Expressions) 라는 정규식 라이브러리를 사용하므로 PCRE 문법으로 정규식을 기술하면 된다.

다음은 오픈소스 CMS 인 워드프레스의 로그인 페이지(wp-login.php)를 정해진 IP 에서만 호출 가능하게 설정하는 예제이다.

<Files ~ "^wp-login.php">

Order Deny,Allow
Deny from all
Allow From 127.0.0.1 192.168.1.0/24

</Files>

Files 와 비슷한 지시자로 FilesMatch 가 있다. 사용법은 유사하며 차이점은 FilesMatch 지시자는 ~ 문자를 안 써도 파일명에 정규식을 사용할 수 있는 점이다.


Default Type

기본 MIME 타입을 지정한다. 모르는 형식의 파일이 있을 경우 웹서버는 HTTP 헤더중 application 부분을 여기 설정된 MIME 타입으로 지정하게 되므로 "application/text/plain" 으로 설정하게 된다.



LogFormat

서버가 생성하는 로그 형식에 대해 여러 개의 형식을 지정해 놓고 별명을 붙여 놓았다. 가장 많이 사용되는 로그 포맷은 common 이며 "%h %l %u %t \"%r\" %>s %b" 포맷에 대한 별명이다.

combined 는  common 로그 형식에 HTTP 리퍼러와 User-Agent 를 추가한 포맷이다. 포맷 항목의 의미는 아래와 같다.

ErrorLog

에러 발생시 남길 로그 파일의 경로이다.


CustomLog

클라이언트의 접속과 요청 이력을 남길 파일의 경로이다. "CustomLog logfile LogFormat" 형식으로 설정하며 LogFormat 은 위에서 설정한 별명을 사용할 수 있다.

기본값은 common 이며 User-Agent 와 리퍼러를 남기려면 combined 를 사용하면 된다.


Alias

URL 을 파일 시스템에 연결하는 지시자이다. 사용법은 Alias 별명 디렉터리명 이다. 기본 설정에 있는 다음 내용은 /icons/ URL로 들어온 요청을 "/var/www/icons/" 로 연결한다.

Alias /icons/ "/var/www/icons/"

설정시 주의할 점은 별명에 / 가 붙었다면 디렉터리명에도 / 를 붙여야 하며 별명에 / 가 없다면 디렉터리명에도 / 가 빠져야 한다.


AddDefaultCharSet 

가상호스트마다 설정할 수 있는 지시자로 컨텐츠 형식이 text/plain 이나 text/html 일 경우 HTTP 응답 헤더에 다음과 같이 DefaultCharSet 에 설정된 charset 헤더를 추가한다.

CentOS 의 아파치 기본값은 UTF-8 이므로 다음 헤더가 자동으로 추가되어 브라우저에 전송된다.

Content-Type: text/html; charset=UTF-8

주의할 점은 HTTP 응답 헤더에 charset 이 지정되어 있을 경우 HTML 내 meta 태그로 charset 이 지정되어 있어도 브라우저가 무시한다는 것이다. 그러므로 AddDefaultCharSet 이 UTF-8 로 설정된 웹 사이트에서 EUC-KR 로 된 HTML 이 있을 경우 한글이 깨져 보이게 된다. 

DefaultCharSet과 다른 HTML 파일이 있는 경우 meta 로 charset 을 명시적으로 지정하고 AddDefaultCharset Off 로 설정해야 한다.


가상 호스트(Virtual Host)

웹서비스를 시작하려면 가장 중요한게 컨텐츠와 어플리케이션이지만 서비스를 제공할 장비도 있어야 한다. 여러 개의 도메인을 가진 각각의 서비스가 있을 경우는 어떻게 해야 할까.

각각의 서비스마다 장비를 구매해서 사용해도 되지만 서비스 규모가 작거나 기존 장비의 성능이 남을 경우 한 장비에서 여러 웹서비스를 구동하고 싶어질 것이다.

가상 호스트는 이것을 가능하게 해 주는 기술로 웹서버 운영시 가장 많이 설정하게 되는 중요한 기능이다. 먼저 가상 호스트의 종류를 알아보자.


IP 기반 가상호스트

하나의 웹서버에 여러 개의 IP 를 할당하고 IP 별로 가상호스트를 사용하는 방법이다. 간단한 방법이지만 새로운 웹 사이트가 추가될 경우 이더넷 카드에 IP 를 추가 할당하고 네트워크 설정 및 아파치 설정을 변경해야 한다.

Listen 192.168.0.3:80
Listen 192.168.0.4:80


<VirtualHost 192.168.0.3:80>
	ServerAdmin webmaster@server1.example.com
	DocumentRoot /var//www/server1
	ServerName server1.example.com
	ErrorLog logs/server1-error_log
	TransferLog logs/server1-access_log
 </VirtualHost>


<VirtualHost 192.168.0.4:80> 
	ServerAdmin webmaster@server2.example.com
	DocumentRoot /var//www/server2
	ServerName server2.example.com
	ErrorLog logs/server2-error_log
	TransferLog logs/server2-access_log 
 </VirtualHost>
CODE

가상 호스트를 추가 변경할 경우 서버에 IP 를 새로 할당해야 하고 그외에 관리해야 할 부분이 많고 불편하므로 잘 사용되지 않는 방식이다.

Port  기반 가상 호스트

가상 호스트별로 포트를 달리 해서 서비스 하는 방식이다. 포트별로 아파치를 띄우거나 아니면 Listen 지시자에 사용할 여러 포트를 기술한 후에 VirtualHost 에 포트를 지정하면 된다.

가장 불편한 사항중 하나는 이런 방식으로 웹서비스를 제공할 경우 사용자가 웹 서비스의 포트를 기억해야 하는 점이다. 일반적으로 사용자는 서비스의 도메인 이름만 기억하고 포트를 기억하지 못하므로 사용자 친화적인 방법이 아니며 설정이 불편하므로 잘 쓰이지 않는다. 

다음은 8080, 9090 두 개의 포트를 사용하여 포트기반 가상 호스트를 설정하는 예제이다.

Listen 8080
Listen 9090

<VirtualHost 10.0.1.2:8080>
	DocumentRoot /var/www/vhosts/port8080
</VirtualHost>

<VirtualHost 10.0.1.2:9090>
	DocumentRoot /var/www/vhosts/port9090
<VirtualHost>
CODE


이름 기반 가상호스트

한 장비에 여러 개의 도메인 이름을 주고 도메인 이름을 기반으로 가상호스트를 제공하는 방식이다. IP 가 하나라도 여러 개의 도메인 이름을 줄 수 있으므로 웹서버 관리자는 가상호스트 설정에 도메인 명을 지정하면 된다.

동작 원리

이름 기반 가상호스트는 클라이언트가 HTTP 요청시 전송하는 헤더중 Host: 헤더를 사용한다.

사용자가 브라우저의 주소창에 app.example.com 이라고 입력하면 브라우저는 DNS 에서 서버의 IP 를 찾아서 연결한 후에 그림에서 박스 친 부분처럼 HTTP 요청 헤더를 보내게 된다. 

이때 브라우저는 Host: 헤더에 요청하는 도메인 명을 보내므로 웹 서버는 이를 해석해서 이름에 맞는 가상 호스트 설정에 따라 컨텐츠를 전송한다.

만약 브라우저가 보낸 Host: 에 있는 서버의 명이 설정에 없다면 첫번째 가상 호스트에 있는 설정을 참고하여 요청을 처리하게 된다.


설정

가상 호스트 설정은 웹서버 운영시 많이 하는 작업이므로 필자의 경우 가상 호스트 설정은 별도의 파일(conf/httpd-vhost.conf)로 분리한 후에 Include 지시자로 포함시켜 사용하고 있다.

httpd.conf 의 마지막에 다음과 같이 Include 문을 사용하면 된다.

Include conf/httpd-vhost.conf

이제 분리한 설정 파일에 가상 호스트를 지정해 보자. 가상 호스트 지정은 <VirtualHost> 지시자를 사용하여 설정한다. 위에서 IP 기반의 가상 호스트 설정을 이름 기반으로 변경해 보자.

NameVirtualHost *:80

<VirtualHost *:80>
	ServerName server1.example.com
	DocumentRoot /var//www/server1

	ErrorLog logs/server1-error_log
	TransferLog logs/server1-access_log
 </VirtualHost>

<VirtualHost *:80> 
	ServerName server2.example.com
	DocumentRoot /var//www/server2 
	
	ErrorLog logs/server2-error_log
	TransferLog logs/server2-access_log 
 </VirtualHost>
CODE


  • NameVirtualHost *:80
    80 포트에서 이름 기반의 가상 호스트를 사용하겠다는 의미로 꼭 적어줘야 제대로 동작한다.
    만약 클라이언트가 설정되지 않은 호스트 이름을 보냈을 경우 기본 처리할 가상 호스트로 지정하려면 *:80 대신 _default_:80 을 적어주면 된다.
    _default_로 지정된 가상호스트가 없을 경우 모르는 호스트 요청이 들어오면 맨 위에 설정된 가상 호스트가 서비스하게 된다.

    아파치 웹 서버 2.4부터는 기본 설정이 이름 기반 가상호스트이며 NameVirtualHost 지시자도 삭제되었다.

     

  • <VirtualHost *:80> 
    가상 호스트 지시자로 이름 기반의 호스트이므로 IP 를 적지 않고 *와 포트를 적어주면 된다.
  • ServerName
    가상 호스트가 구분할 서버이름이다. 전체 도메인 이름으로 적는다.
  • ServerAlias 
    하나의 가상 호스트에 .com 과 .co.kr 도메인을 같이 제공하는등 여러 개의 ServerName 을 줘야 할 경우가 있다. 이럴 경우 ServerAlias 로 추가할 서버이름을 적어 주면 된다. 주의할 점은 ServerAlias 는 여러 개를 적어도 되지만 ServerName 은 하나만 가능하므로 여러 개를 적었을 경우 마지막 적은 ServerName 이 적용된다.
  • ErrorLog, TranferLog 
    기본 값인 access_log 나 error_log를 사용하면 모든 가상호스트의 로그가 하나의 파일에 남으므로 가상 호스트별로 로그 파일을 분리하는게 관리 측면에서 용이하다.


기타 Location 이나 Directory, DirectoryIndex 등의 지시자도 가상 호스트마다 설정 가능하다.


이제 service httpd restart 명령으로 웹서버를 재구동한 후에 가상 호스트로 연결하면 각각 다른 컨텐츠가 보인다면 정상적으로 설정된 것이다.


DNS 등록

가상 호스트에 사용할 도메인 이름을 DNS 에 등록해야 테스트할 수 있지만 DNS 등록 권한이 없을 경우 hosts 파일에 등록하여 사용할 수 도 있다.

server1.example.com, server2.example.com 의 IP 가 192.168.152.2 일 경우 hosts 파일에 다음과 같이 등록해 주면 DNS 에 등록한 것과 동일한 결과를 얻을 수 있다.

192.168.152.2 server1.example.com

192.168.152.2 server2.example.com

hosts 파일은 사용하는 OS 의 종류에 따라 위치가 약간 다르다. 윈도라면 윈도 설치 경로(일반적으로 C:\Windows) 에서 system32\drivers\etc\hosts 파일이며 리눅스나 Mac OS X 는 /etc/hosts 파일을 사용하면 된다.

이 방식의 단점은 모든 클라이언트의 hosts 파일 설정을 변경해야 하는 점이지만 DNS 가 없거나 변경할 권한이 없는 경우 유용하게 사용할 수 있다.