Search
💤

Inception

Created
2022/04/13
tag
42서울
42Seoul
Inception
VM
Docker
DockerCompose
Inception을 작성하면서 겪었던 자잘한 이슈들은 옆 카테고리의 Reference 링크에 작성해두었습니다. 만일 찾아도 해결이 되지 않는 이슈가 있다면 Reference 쪽을 참고해보세요. SA라고 해야하나... DevOps라고 해야하나... 이 쪽 계열은 흥미로운데 언제 해도 힘드네요 ㅎㅎ Docker 관련 지식을 처음부터 쌓고 싶다면 아래 링크를 이용해보세요.

Subjects

1. Virtual Box

아래는 우분투와 비교 글인데 정말 가볍게 읽어보면 좋을 것 같다. VM에서 이용할 수 있는 OS에 대한 명시는 따로 없으므로 어떤 운영체제를 이용해도 되겠지만, 상대적으로 더 적은 용량을 차지하는 debian이 나쁜 선택은 아니라는 생각이 들었다. (Dockerfile의 기반 이미지와는 다르다. Dockerfile에서는 대체적으로 용량이 적은 alpine 리눅스를 이용했다.)

1) Debian 10 Installation

Debian 10 설치는 아래 링크의 seungoh님의 글이 잘 쓰여져 있으므로 이를 참고하자.
놓치기 쉬운 주의할 점으로 원활한 Intra에서의 평가를 위해 패키지 설치를 위한 Software Selection 시에 반드시 Debian desktop environment, print server, SSH server, standard system utilities 항목들을 체크하여 진행하도록 하자.

2) Configuration

1. Package 설치

# 사용자 전환 su - # Repository 인덱스 업데이트 apt-get update # sudo 설치 apt-get install -y sudo # 패키지 관리자가 https를 이용할 수 있도록 설정 sudo apt-get install -y apt-transport-https # SSL 통신이 가능하도록 CA (Certificate Authorities)로부터 인증서를 획득 sudo apt-get install -y ca-certificates # 다양한 통신 프로토콜을 지원하는 데이터 송,수신 패키지 sudo apt-get install -y curl # Repository를 추가 및 삭제할 수 있도록 설정하는 패키지 sudo apt-get install -y software-properties-common # git 설치 sudo apt-get install -y git # make 설치 sudo apt-get install -y make # vim 설치 sudo apt-get install -y vim # systemd 설치 sudo apt-get install -y systemd
위 명령어들을 이용하여 패키지들을 설치하고, 필요한 것들이 있다면 적절히 추가하면 된다. 이와 같은 명령어들을 묶어서 Shell Script로 묶으면 조금 더 편리하게 진행할 수 있다.

2. Docker 설치

# Repository 인덱스 업데이트 sudo apt-get update # 도커 패키지 신뢰성 추가 # f - http의 요청 헤더의 Content-Type을 multipart/form-data로 설정 # s - silent로 진행과정 및 오류 생략 # S - SSL 이용 # L - curl의 결과가 Redirection이더라도, 해당 URL로 작업을 이어감 # 파이프 이후의 apt-key는 다운로드 받은 패키지를 인증된 패키지로 인식 시키기 위해 키 리스트에 패키지를 추가 # (apt는 기본적으로 인증된 패키지들을 키 리스트로 관리) sudo curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - # 도커를 설치할 수 있도록 Repository 추가 sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" # Repository 인덱스 업데이트 sudo apt-get update # 도커 설치 sudo apt-get install -y docker.io # 도커 소켓 /var/run/docker.sock 권한 666 설정하여 다른 사용자도 접근할 수 있도록 변경 sudo chmod 666 /var/run/docker.sock # 도커 컴포즈 설치 (1.29.2 버전) sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose # 도커 컴포즈 실행 권한 부여 sudo chmod +x /usr/local/bin/docker-compose # 도커 서비스 재시작 시 컨테이너를 종료하지 않도록 설정 sudo su -c 'printf "{\n\t\"live-restore\": true\n}" > /etc/docker/docker.json’ # 도커 서비스 재시작 sudo systemctl restart docker
위 명령어들을 이용하여 DockerDocker Compose를 설치하고, 필요한 것들이 있다면 적절히 추가하면 된다. 이와 같은 명령어들을 묶어서 Shell Script로 묶으면 조금 더 편리하게 진행할 수 있다.

3. Clipboard

Ubuntu의 경우 Guest Image를 이용하여 내용물을 설치했을 때 Clipboard 공유가 Host OS와 가능하도록 쉽게 설정할 수 있는데, 이상하게 debian의 경우에는 Clipboard 공유를 위해 Guest Image를 삽입하여 설치를 진행하면 위 그림처럼 오류가 발생할 때가 있다. 이 때는 수동으로 Guest ImageMount하여 설치하면 된다.
우선 아래 그림처럼 Guest ImageVM의 설정에 들어가서 저장소의 광학 이미지로 추가할 필요가 있다.
# Repository 인덱스 업데이트 sudo apt-get update # 커널 모듈을 빌드하기 위한 패키지 설치 (uname -r은 커널의 버전을 의미) sudo apt-get install -y build-essential dkms linux-headers-$(uname -r) # 광학 이미지를 Mount 하기 위한 디렉토리 생성 sudo mkdir -p /mnt/cdrom # 광학 이미지를 Mount sudo mount /dev/cdrom /mnt/cdrom # Mount된 디렉토리로 이동 cd /mnt/cdrom # Guest Image의 실행 파일을 직접 실행 (nox11은 xterm 화면이 나타나지 않도록 설정) sudo sh ./VBoxLinuxAdditions.run —nox11
위 과정까지 잘 진행 되었다면, 아래 그림처럼 클립보드 공유드래그 앤 드롭 항목을 양방향으로 설정해준다.
# 재부팅 sudo shutdown -r now # 결과 확인 lsmod | grep vboxguest
그리고 위 명령어로 VM을 재부팅하고 하고 나서 grep을 실행 했을 때 아래 그림처럼 나타난다면, 정상적으로 Clipboard 공유가 이뤄진 것이다.
복사 키는 ctrl + insert, 붙여넣기는 shift + insert로 수행한다. Mac OS X에서 insert 키가 없는 경우에는 insert 키가 fn + enter로 되어 있으니 당황하지 말자.

4. Sudoer

debian을 설치할 때 잘 따라왔다면, root 계정과 일반 사용자 계정을 따로 만들었던 것을 기억할 것이다. 물론 root 계정을 이용하게 되면 큰 문제는 없지만, 로그인 사용자 계정으로도 Docker Compose를 원활히 이용하기 위해선 일반 사용자를 Sudoer로 등록함과 동시에 root 그룹에 등록하여 root 소유의 파일과 디렉토리에 접근할 권한을 얻는 것이 필요하다.
# 사용자 전환 su - # 사용자를 sudo 그룹에 추가 sudo usermod -a -G sudo $intra_id # 사용자를 root 그룹에 추가 sudo usermod -a -G root $intra_id
위 명령어를 이용하여 일반 사용자 계정을 적절히 그룹에 추가한다.
# visudo를 이용하여 /etc/sudoer를 수정 sudo visudo
위 명령어를 이용하면 sudoer로 사용자를 추가할 수 있는데, 아래와 같은 형태로 파일 제일 아래에 작성하면 된다. 각 필드는 |로 연결된 항목을 의미한다.
jseo ALL=(ALL:ALL) ALL | | | | | | host | | all commands username | | | all groups all users
Plain Text
복사
# 사용자 전환 su - $intra_id
다시 사용자를 전환하여 root 계정에서 일반 사용자 계정으로 돌아오면, 문제 없이 root 파일과 디렉토리에 접근할 수 있는 것을 볼 수 있다.

3) Snapshot

아래 기재된 링크를 참조하여 복원과 복제를 활용할 수 있다.

1. 복원

이전 항목에서 설정해둔 vdi에 반드시 Snapshot을 생성해둘 수 있도록 하여 서브젝트 진행에 고통받지 않도록 하자.
내 경우에는
초기 설치에 대한 Snapshot 1회
Package 설치에 대한 Snapshot 1회
Docker 설치에 대한 Snapshot 1회
Clipboard 공유에 대한 Snapshot 1회
Sudoer 설정에 대한 Snapshot 1회
FTP 툴 설치에 대한 Snapshot 1회
로 설정마다 Snapshot을 남겼다.
이렇게 남긴 Snapshot을 이용하여 VM 상에서 문제가 생기더라도 유용하게 복원을 시도할 수 있다.

2. 복제

평가를 진행할 때도 VM에서 진행하기 때문에, 로컬에서 VM에 설정한 환경을 그대로 옮길 필요가 있다. 이는 Virtual Box의 복제 기능으로 해결할 수 있다. 방법은 위 링크에 기재된 방법을 그대로 따르면 되고, 주의할 점만 몇가지 언급해보려 한다.
복제 시에는 완전한 복제를 선택하여 진행하고, 복제할 vdi의 가상 머신이 이미 존재하는 경우에 MAC 주소와 하드웨어의 UUID가 겹칠 수 있는 문제 때문에 모든 네트워크 어댑터의 새 MAC 주소 생성을 선택하고, 디스크 이름이 이상하게 바뀌는 것이 싫다면 디스크 이름 유지하기를 선택 후 복제를 진행한다.

2. Mandatory

1) PID 1

Docker Container를 한 번쯤 만들어봤다면, 분명 명령어를 Entrypoint 혹은 Command로 기재 했음에도 종료된 상태가 되는 것을 경험해본적이 있을 것이다. 이에 대한 이해를 먼저하고 넘어가자.
VM 대신에 Docker를 이용하는 여러 이유들 중에서 빼놓을 수 없는 사항은 PID 1이다. 이는 PID 1로 등록된 단 하나의 프로세스만을 Container가 담당하겠다는 의미를 갖고 있으며, 이에 따라 단 하나의 Container가 곧 단 하나의 서비스 (프로세스)를 의미하여 서버의 환경 관리 측면에서 일관성을 갖게 한다.
즉, 위와 같은 측면에서는 임의로 Loop를 생성하여 돌린다거나, tail -F anything, bash 와 같은 행위가 원 목적과 얼마나 어긋나는지 알 수 있을 것이다. Container의 실행이 곧 서비스의 실행으로 이어저야 하는데 이외의 행위는 그렇지 못한 것이니 말이다. 이 때문에 서브젝트에서는 이와 같은 무의미한 명령어 구성을 금지하는 것이다.
그렇다면 단순히 서비스로 동작시켜야 하는 프로세스를 띄우면 되는데 왜 위와 같은 행위를 시도하게 되었을지 생각해봐야 하는데, 이는 데몬 프로세스와 관련이 있다. 기본적으로 Unix에서의 프로세스 구조를 보면 루트가 되는 PID 1부터 시작하여, 자식을 만들어가는 형태로 트리 구조를 갖는다. 일반적인 사용에서는 이와 같은 구조가 문제될 일이 크게 없겠지만, 머신 상에서 유지하고 있는 서비스 (예를 들어 docker.d, mysql.d와 같은)는 문제가 될 수 있다. 트리 구조로 유지되는 프로세스들은 부모 프로세스가 죽으면 모두 종료되는데, 만일 서비스로 실행하고 있는 프로세스가 부모 프로세스의 종료로 이용할 수 없게 된다면 이를 이용하고 있는 여타 다른 프로세스에 문제가 생긴다. 이에 따라 서비스로 유지해야 하는 프로세스들을 별도의 루트를 만들어 동작시키는데, 이를 데몬 프로세스라고 한다.
따라서 데몬 프로세스는 기본적으로 PID 1로써 실행하는 것이 아니니, 당연히 Docker Container에서는 어떠한 것도 실행할 것이 없는 것으로 보고 종료된 상태가 반복되는 것이다. 그렇다면 데몬 프로세스로 실행되는 것들을 일반 프로세스로 실행하면 되지 않는가? 맞는 말이므로 이와 관련된 옵션들을 찾아볼 필요가 있다. 대체적으로 데몬 프로세스들은 이와 같은 기능을 제공한다.

2) Container OS

Container에서 기반으로 삼을 OS는 서브젝트 명시에 따라 debian 10 (buster) 혹은 alpine을 사용할 수 있는데, 지금처럼 서비스를 구성할 때 Container 상에서의 가벼운 OSalpine을 많이 선택하여 써왔기 때문에 비록 Host OSdebian 10을 썼지만 alpine을 선택했다.
그리고 버전의 경우 간단한 서비스를 구성하고 싶을 때는 alpine 이미지를 latest를 골라서 사용했지만, 지금처럼 Bonus까지 고려하여 많은 서비스를 엮어내야 할 때는 이미지 선택이 상당이 조심스러워야 한다. OS의 버전에서 어떤 이슈가 있었는지, 언제까지 지원이 되는지를 고려해야 한다. 따라서 내 경우에는 아래 링크에서 stable 버전을 찾고, 아직 지원 기간이 조금 넉넉하게 남은 버전들 중에서 가장 패치가 많았던 버전을 골라서 이용했다. 따라서 대체적으로 alpine 3.13을 이용하여 Container를 구성했다.

3) Dumb Init

만일 개발자가 작성한 소스 코드를 빌드하여 Docker Container에서 이를 서비스로 이용하고 있는 상황이라면, Dumb Init이 필요한 이유에 대해서 공감할 수 있다.
빌드된 바이너리를 실행하여 서비스로 이용하는 것에는 큰 문제는 없어보이지만, 소스 코드 상에서 시그널 핸들링이 잘 처리 되어 있지 않다면 문제가 될 수 있다. 이는 당연히 이전 항목에서 언급한 데몬 프로세스에도 해당된다. 바이너리에서 시그널을 이용하도록 만들지도 않았는데 이와 같은 것이 중요한지 궁금할 수 있겠지만, 꽤나 합리적인 이유가 있다.
기본적으로 PID 1SIGTERM에도 기본 동작을 수행하지 않도록 되어 있기도 하고, 또한 이 때 PID 1로 실행하려는 바이너리가 SIGTERM에 대해서 자체적인 핸들링 로직이 없다면 SIGTERM은 실행 중인 프로세스에 아무런 영향을 끼치지 못하게 된다. 따라서 당연히 SIGTERM을 이용하여 Container를 종료하는 등의 작업을 수행하고 싶어도 할 수 없게 된다.
위에서 언급된 시그널 같은 작업은 CI에서 활용될 수 있으므로, Container에서 동작하는 프로세스에게 시그널을 보낼 수 있도록 만들어주는 역할은 꽤나 중요하다고 볼 수 있다. 이와 같은 작업이 가능하도록 만들어 주는 것이 Dumb Init이다. Dumb Init은 시그널 핸들러가 잘 짜여진 바이너리가 아니라면 꽤나 유용하게 활용 되는데, 이는 데몬 프로세스에게도 해당된다.
데이터베이스와 관련된 데몬 프로세스들은 대체적으로 좋은 시그널 핸들러를 갖고 있다고 한다.

4) Docker Compose

Docker Compose를 사용하는 목적에 대해서 Docker Engine을 사용하여 만든 단순 Container와 어떤 차이점이 있는지 꼭 짚어가며 공부하길 바란다. 특히 Docker Compose 내용은 굉장히 방대하므로 아래 링크의 Specification을 참고하여 어떤 항목의 설정이 가능하고, 해당 설정이 어떤 영향을 끼치는지 꼭 찾아보는 것을 권한다.

1. Docker Compose Version

Docker Compose1.x, 2.x, 3.x 버전으로 나뉘게 되며, 각 버전마다 기능들이 상이하므로 이용하려는 버전의 기능을 명확히 인지하고 사용하는 것이 좋다. 내 경우에는 Docker Compose ←→ Docker Swarm 간의 호환이 가능한 3 버전을 이용한다.

2. .env

Docker Compsoe의 사용 목적을 생각해보면 Health Check, Recovery, Event Tracking 등 여러 요소가 있겠지만, 그 중에서도 일단 단순 Docker를 이용하여 Container를 생성할 때의 장황한 옵션을 사용하지 않도록 만드는 점도 있다. 이에 따라 yml 파일을 활용하, yml 파일 상에서는 설정된 환경 변수를 읽어오도록 만드는 것이 가능하다. 그렇다면 설정되지 않은 환경 변수를 이용하려는 경우에는 어떻게 해야할까.
다행스럽게도 환경 변수로 이용하려는 값을 .env에 명시하여 작성하게 되면, Docker Compose는 서비스로 유지하려는 Docker Container를 생성할 때 해당 값들을 자동으로 읽어서 이용할 수 있도록 만들어 준다. 따라서 사전에 환경 변수를 정의한다거나 환경 변수를 지정하는 Shell Script를 만들 필요 없이 .env 만 운용하면 되고, 값을 바꿀 필요가 있는 경우에는 .env 만 수정하면 된다.

3. Network

Docker에서 이용할 수 있는 네트워크 모드는 host, none, bridge가 있다고 했다. Docker Compose에서도 network_mode를 이용하여 이를 정할 수 있으며, 특히 여러 네트워크를 생성하여 서비스에 여러 네트워크를 적용하는 것도 가능하다. 즉, 기재된 네트워크가 같은 서비스 간에는 해당 네트워크를 이용하여 통신을 할 수 있는 것을 의미한다.
Docker Compose에서 네트워크를 정의하는 것도 가능하지만, Docker Compose와 별개로 Docker Engine으로 생성한 네트워크를 이용하는 것도 가능하다. 참고로 Docker Compose에서 정의한 네트워크는 Docker ComposeUp과 함께 생성되었다가 Down과 함게 삭제된다.

4. Volume

Docker Engine을 이용했을 때처럼 임시 파일 시스템을 구성하여 이용하는 tmpfs를 이용할 수도 있고 Docker Volume을 생성하여 이용하는 것도 가능하며, Host의 디렉토리를 Mount 하여 이용하는 것도 가능하다. 이 때 최후자인 Mount를 이용하는 경우, 리눅스 상의 mount 명령어와 동일하게 동작하므로 bind 여부를 결정하여 이용하는 것도 가능하다.

5. Dockerfile with Alpine

debian 계열의 리눅스만 이용했던 사람들은 alpine이 익숙하지 않아서 패키지 관리자인 apk가 다소 낯설 수 있다. 아래 링크를 참고하여 명령어를 보다 쉽게 이해하고 구성할 수 있다.

5) Makefile

Makefile은 당연하게도 C, C++에서만 이용할 수 있는 것이 아니다. 단순히 Rule에 대한 Depency와 명령어 묶음일 뿐이므로 Docker Compose에서 이를 이용하는 것이 그리 이상한 것은 아니다.
내 경우에는 이름에 대한 Rule에선 Docker Compose로 이미지를 다시 build 하도록 만들었고, 서비스로 유지하고 있는 Docker Container도 다시 만들어 백그라운드로 동작시키도록 만들었다.
그리고 clean에 대한 Rule에서는 Docker Compose로 구성하고 있는 Container들을 삭제하도록 down을 적용했고, fclean 시에는 all을 했을 때 새로운 환경에서 이용하는 것처럼 만들기 위해 기존에 생성했던 파일과 디렉토리를 지우고 Docker 관련 (Volume, Network 등) 을 모두 지우도록 만들었다.
당연히 fcleanall을 적용하는 re에 대한 Rule도 만들었는데, 이와 같은 작업이 all에서 늘 --force-recreate --build를 수행하기 때문에, Container의 재생성이 필요없으면서 이미지를 다시 만들 필요가 없을 때는 불필요하게 시간을 기다리게 될 때가 있다. 따라서 단순히 Docker Compose로 구성하고 있는 Container들을 다시 껐다가 켜면서 Entrypoint + Command만 다시 실행시켜주는 restart라는 Rule도 만들었다.
추가적으로 Docker Compose로 유지하고 있는 Container들의 상태와 이벤트 기록들을 파악하기 위해 ps, log라는 Rule들을 추가하였다.

6) MariaDB

1. MariaDB Environment Variables

MariaDB에서 활용하는 환경 변수 항목들은 아래 링크에서 소개한다. 이를 잘 참고하여 네이밍을 할 수 있도록 하자.

2. mysql-install-db

MariaDB와 관련된 데몬과 클라이언트를 설치하면, 시스템 상에 필요한 내용들을 자동으로 만들어주는 식의 일련의 과정을 수행하는 mysql-install-db 를 활용할 수 있다. 사용할 수 있는 여러 옵션들 중에서도 —auth-root-authentication-method를 이용하여 초기 설정을 위한 인증을 진행할 수 있고, 볼륨으로 지정해둔 디렉토리를 —datadir를 이용하여 해당 디렉토리에 데이터를 둘 수도 있다.
자세한 옵션들은 아래 링크를 참고하자.

3. mysqladmin

위처럼 초기 설정을 위한 명령어를 실행한 후에는 MariaDB에서 관리자가 수행해야 하는 역할들을 쉽게 이용할 수 있도록 mysqladmin을 활용할 수 있다. mysqladmin을 이용하면 root 사용자의 패스워드를 바꾼다거나, 일반 사용자의 패스워드를 바꾼다거나, 데이터베이스의 조작, 캐쉬 동기화, 커넥션 관리 등 다양한 작업을 수행할 수 있지만, 그 중에서도 MariaDB 데몬이 살아있는지 죽어있는지 상태를 확인할 수 있다는 점이 유용하다.
물론 Docker Container로 실행되고 있는 각 서비스들 사이에 depends_on이라는 옵션을 이용하여 의존성을 둘 수 있지만, 모든 작업을 Dockerfile 상에서 이룰 수는 없는 것이고 (예를 들어 환경 변수를 받아서 하는 작업은 Docker Container에 환경 변수를 할당 받은 후에야 활용하는 것이 가능), 따라서 이와 같은 작업들이 Dockerfile이 아닌 Shell Script 상에서 이뤄질 때는 해당 Container에 종속된 다른 서비스에서 MariaDB의 상태를 확인하는 과정이 반드시 필요하다. 이와 같은 경우에 유용할 수 있다.
사용 예시는 아래 링크에서 자세히 확인할 수 있다.

4. Eval

Docker Compose의 목적에 맞게 이용하려면 MariaDB에서 사용하려는 각 리터럴 값 (예를 들면 데이터베이스 이름, 호스트, 사용자, 패스워드 등)을 상황에 맞게 이용하더라도 동일하게 동작하는 것을 보장할 수 있어야 한다. 즉, 입력이 반드시 필요한 설정 파일을 제외하고는 별도의 파일 설정이 없더라도 동일한 환경을 제공할 수 있어야 한다. 따라서 이후에 이어질 WordPress를 위한 MariaDB의 데이터베이스 생성 및 사용자 생성 등 역시 리터럴로 기재하는 것이 아니라 Docker Compose에서 넘겨 받은 환경 변수를 이용하는 것이 바람직하다.
Docker Compose에서 환경 변수를 받는다고 하더라도 이를 Query가 작성된 SQL 파일에 적용하는 것이 여간 쉬운 것은 아닌데, Shell Script에서의 eval 명령어를 이용하면 용이하게 처리할 수 있다. SQL 파일에서는 환경 변수로 대치되어야 하는 값들을 $ 표기로 이용하여 작성하고, 해당 파일을 실제로 이용하는 Shell Script에서는 catechoeval 과정을 거쳐서, 환경 변수로 대치된 값을 유지하는 텍스트를 MariaDB로 실행시키면 된다.

5. mariadb-server.cnf

데이터베이스를 구성하는데 있어서, 내부적으로만 활용하는 경우는 거의 없다. 적어도 관리 툴을 이용하여 특정 권한을 가진 사용자들은 데이터베이스에 원격으로 접속하는 경우가 있으므로 이에 대한 설정을 할 수 있어야 한다. Inception에서는 이와 관련된 조건 명시가 없으므로 어느 곳에서든 접속할 수 있도록 mariadb-server.cnfbind-address 라는 항목을 0.0.0.0으로 설정했지만, 이를 적절히 바꾸어 운용할 수 있다. 특히 이 때는 네트워크를 이용하게 되는데, 별도의 설정이 없다면 기본적으로 MariaDB 데몬은 소켓을 사용하도록 되어 있기 때문에 skip-network 항목을 비활성화 할 필요가 있다.

7) WordPress

1. wp-config.php

Mandatory에서 요구하는 내용들을 작성하면서 php에서 어떤 define을 작성해야 하는지 여간 곤란한게 아니다. 심지어 Bonus 부분을 노린다면 추가적으로 작성해야 할 필드들이 더 있다. 아래 링크에 세세하게 여러 필드에 대한 설명들이 제시되어 있다. 비록 Bonus에서 사용해야 할 필드와는 관련이 없지만, 기본적으로 wp-config.php에서 필요한 필드들은 적절히 설명되어 있다.
WordPressOfficial 웹 사이트에서 Supprot 항목에도 잘 설명이 되어 있으므로 위 링크가 잘 이해되지 않는다면, 아래 링크도 참고해보자.

2. Package

또한 WordPress의 기능을 살려서 이를 View로써 보여주는 CGI는 여러 언어들로도 가능한데, Inception처럼 php를 이용하는 경우엔 이와 관련된 패키지만 설치해야 한다. 다만 Docker의 기반 이미지가 alpine이든 debian이든 설치할 패키지들이 굉장히 많다. 이와 관련해서는 필요한 것들을 일일이 찾아서 기재해도 되겠지만, 적어도 alpine 상에서는 아래 링크에 기재된 패키지만으로 충분히 돌아간다.

3. phar...?

phar가 다소 낯선 워드였는데, php.net에서는 이에 대해서 간략하게 설명해주고 있다. php로 작성한 어플리케이션을 하나의 아카이브로 제공하는 기능이 있는데, 해당 아카이브의 확장자가 phar이다. php에서는 이와 같은 아카이브 파일을 Executable로 바꿔주는 기능도 제공하는 것으로 보인다.

4. wp-cli (a.k.a. wp)

설치할 바이너리의 이름은 wp-cli인데, 개발자들 사이에선 단순히 wp로 많이 (wp-cli를 많이들 wp로 바꿔서 작업) 불린다. 이 때문에 WordPress의 첫 문턱을 넘으려는 초심자들은 안 그래도 Docker가 익숙하지 않아서 DockerfileWorkspaceShell Script에서도 난항을 겪는데, wp-cli를 이용하면서 헛걸음질을 할 때가 많다.
wp-cliphar로 작성된 아카이브 파일이며, 이를 이용하여 WordPress의 여러 작업들을 CLI로 관리할 수 있다. 예를 들어 플러그인 관리 (Bonus에서 이용할 수 있음), 사용자 생성 및 관리, 특정 경로의 WordPress 설치 등이 가능하다. 이와 같은 작업은 core 단위로 동작하며, corepath에 영향을 받는다.
예를 들어 Nginx 상에서 /var/www/wordpress라는 경로로 정적 파일을 제공하고 싶다면, 해당 경로에 정적 파일을 위치 시켜야 한다. 이 때 wp-cli를 이용하여 /var/www/wordpresscore를 다운로드 받고, 해당 경로에 WordPress 설치 및 사용자 작업 혹은 플러그인 설치 및 업데이트를 해주면 된다. wp-cli의 모든 명령어들에게 있어서 path가 중요하다고 했기 때문에 —path 옵션을 반드시 숙지하자.
wp-cli의 간단한 사용 예시는 아래 링크에서 참고할 수 있다.
wp-clicore 명령어와 하위 명령어들은 아래 링크에서 참고할 수 있다.
특히 coreinstall 명령어는 많이 쓰이므로 아래 링크를 참고하자.
wp-cli를 이용한 사용자 생성 및 관리는 아래 링크를 참고하자.
wp-cli를 이용하여 플러그인을 설치한다면, (예를 들어 BonusRedis가 있음) 플러그인을 적용하는 것은 ActivateEnable로 이뤄지는 것을 명심하자. Activate는 플러그인이 이용될 수 있는 상태임을 말하는 것이고, Enable은 이를 사용하는 것을 의미한다. 사용법은 아래 두 링크를 참고하자.
WordPress를 설치하는 전반적인 흐름은 아래와 같다.
혹여나 이어지는 Nginx를 설정하지 않고 서비스 단위로 하나씩 만들어 Docker Compose를 실행해보는 경우에 WordPress로 정적 파일을 만드는데 성공했음에도 불구하고, no such file or directory와 같은 오류를 만난다면, Nginx 설치로 대부분 해결될 수 있다.

8) Nginx

1. Directives

잘 모르는 WordPress로부터 탈출하면 끝인 줄 알았지만, NginxDirectives 설정도 꽤나 만만치 않다. CGI라는 개념을 처음보는 사람들도 있을 것이고, 이에 따라 설정해야 하는 Location의 정규 표현식과 각 인자들이 다소 어지러울 수 있다. 다행스럽게도 아래 링크에서는 php로 활용하는 fastcgi의 여러 인자들이 어떤 역할을 하는지 소개하며, 이에 따라 어떤 인자들이 필수적으로 있어야 하는지 가늠할 수 있다. 특히 fastcgi_pass의 경우 백엔드의 경로를 말하는데, Docker Compose 특성 상 동일 네트워크에 존재하는 서비스들은 서비스 이름만으로 접근이 가능하므로 이를 편하게 이용할 수 있다.

2. Regular Expression on Nginx

NginxDirective를 작성할 때 주로 보이는 정규 표현식이 어떤 의미를 갖는지 궁금하다면 아래 링크를 참고하자.

3. SSL / TLS

VM을 이용하면서 웹 브라우저로 Nginx가 정상적으로 동작하는지 확인하려고 기본적으로 설치된 브라우저를 이용하는 경우가 많을 것이다. 즉, Firefox를 이용하여 https://localhost 혹은 https://$intra_id.42.fr 과 같은 형태로 접속을 하게 될텐데, 만일 이 때 PR_END_OF_FILE_ERROR가 지속적으로 나타난다면 SSL 관련 오류일 가능성이 가장 높다. 즉, SSL 생성 옵션부터 이를 제공하는 Nginx의 설정까지 확인해보는 것이 좋으며, 특히 Nginxconf.d 설정에 문제가 없는지 확인해볼 필요가 있다.

4. Sync

그리고 MariaDB에서 depends_on에 대해서 잠깐 언급했었는데, MariaDB의 의존성을 갖고 있는 WordPress가 반드시 동작하고 있어야 제공할 정적 파일이 있게 되므로 Nginx에서도 문제가 생기지 않는다. 물론 MariaDB, WordPress, Nginx가 한 번에 멋지게 딱 올라가면 좋겠지만, 실제로는 그렇지 못하기 때문에 NginxWordPress의 제공과 얼추 맞춰서 동작하도록 강제로 만들어줄 필요가 있다. 만일 nginx: [emerg] host not found in upstream 혹은 정적 파일 제공과 관련된 오류가 발생한다면 이를 의심해볼 수 있다.
아래 글에서는 depends_on이 없어서 volumes_from을 활용한 것을 볼 수 있다.

5. DNS

여기까지 설정이 되었다면, 문제 없이 https://localhost로 접속하여 WordPress가 제공되는 것을 볼 수 있다. 단, 서브젝트에서 요구하는 Domain 이름으로는 접근이 불가능한데, 이는 /etc/hosts를 설정하여 적용할 수 있다.
아래 명령어를 이용하면 원하는 결과를 볼 수 있게 된다.
sudo echo "127.0.0.1 $intra_id.42.fr" >> /etc/hosts

3. Bonus

Bonus는 총 5개의 항목이 있는데, 1~4번은 명시적으로 수행해야 하는 것들이고, 5~8번은 자율에 맡긴 서비스들이다. 특히 5~8번은 ft_services에서 영감을 얻었다. 아래에 제시된 항목들은 기존 Mandatory와 연관성이 크거나 수행하기 쉬운 것 순서로 작성해뒀다.

1) Redis

1. WordPress Configuration

Redis는 인메모리 데이터베이스로 주로 Cache 작업을 위해서 이용된다. 원래대로라면 Redis의 설치 후에 서비스의 연결을 직접 해줘야 하는데, WordPress에서 이용할 때의 Rediswp-config.php 파일을 통해 연결 설정을 할 수 있기 때문에 상대적으로 간편하다.
Mandatory에서 했던 것과 비슷하게 wp-config.php에는 리터럴 값이 들어가지 않도록 환경 변수를 이용하여 설정하도록 만들었다. 이 때 이용한 값들은 아래와 같다.
WP_REDIS_HOST
WP_REDIS_PORT
WP_REDIS_TIMEOUT
WP_REDIS_READ_TIMEOUT
WP_REDIS_DATABASE
WP_CACHE
기본적으로 Redis를 이용한 Cache 작업을 수행할 수 있으려면 WP_CACHE 값은 반드시 설정해야 하는 것에 주의하고, TIMEOUT 관련 필드들처럼 데이터베이스 설정 시 부가적으로 필요해보이는 것들은 기호에 맞게 이용하면 된다.
각 필드들의 자세한 설명은 아래 링크를 참고하자.
설정 이용 방법을 보고 싶다면 아래 링크를 참고하자.
이와 같이 설정 파일을 수정했다면, WordPress에서 해당 설정 값들을 이용할 수 있도록 Redis 플러그인을 설치하고 활성화 및 적용하여 서비스를 구동하면 된다. 이전에 설치했던 WordPress 서비스의 wp-cli를 이용하면 된다. 아래 링크를 참고하여 Redis 플러그인을 설치하여 이용하자.

2. Redis Configuration

WordPress의 설정 파일을 작성했다고 해서 바로 Redis를 이용할 수 있는 것은 아니고, Redis의 설정도 필요하다. 설정 파일의 경로는 /etc/redis.conf이며 보안을 위해 필수적으로 알아둬야 하는 필드들은 3가지이다. bind, protected_mode, requirepass이다.
bindRedis를 구동하고 있는 서버의 여러 네트워크 인터페이스 중에서도, 어떤 IP 주소로 요청을 받도록 할 것인지 결정지을 수 있게 해주는 값이다. 즉, 어느 인터페이스 Listen을 하도록 만들 것인지 결정 짓는 필드라고 볼 수 있다. 클라이언트의 입장에서는 해당 IP 주소로만 접속을 할 수 있게 된다.
bind가 되어 있다면 무조건 설정된 IP 주소로만 접속이 가능하기 때문에 괜찮지만, bind가 설정되어 있지 않다면 protected_mode의 설정이 꽤나 유의미하다. bind가 되어 있지 않았다고 했을 때, protected_mode가 설정되 있을 때는 127.0.0.1로만 접속할 수 있고 (즉, 로컬 클라이언트만 받음), 그렇지 않을 때는 서버에 존재하는 모든 인터페이스로 접속이 가능하다.
requirepass의 경우 Redis 접속을 위한 패스워드를 지정하는 것인데, 패스워드를 지정하게 되면 패스워드의 인증 절차를 우선적으로 거쳐야 한다.
내 경우에는 bindprotected_mode 만 설정해두었다. 그리고 조금 더 들어가보자면, 접속이 가능하지 않은 인터페이스 중에서도 Listen을 하고 있는 경우가 있을 수 있기 때문에 bind 만큼은 반드시 설정해줄 수 있도록 하자.

3. Docker Compose Configuration

Redis를 서비스로써 이용하기 위한 설정 값들은 이전 항목들에서 설정한 것이면 되고, Docker Compose로 서비스를 위한 Container를 생성하는 것들을 살펴보자.
WordPress에서 설정 값을 건드릴 때 환경 변수를 이용한다고 했기 때문에, 필드에 이용했던 값들은 모두 .env에 추가되어 있어야 한다. 그리고 .env에 정의된 환경 변수 값들을 이용하도록 WordPress 서비스의 환경 변수 필드에 추가적으로 잘 명시해주었다.
Redis 서비스의 경우엔 별도로 환경 변수를 명시하지 않도록 작성했고, 다른 서비스들과 크게 다르지 않은 형태로 작성했다. 다만 내 경우에는 서비스 단위로 필요한 네트워크를 쪼개 두었기 때문에, 이번에도 마찬가지로 Redis를 이용하는 네트워크를 생성하여 이를 RedisWordPress에 추가하였다.

2) FTP

리눅스에서 FTP를 이용하려고 하면 주로 proftp, vsftp로 갈리는 것으로 알고 있는데, 내 경우에는 속도, 보안, 성능에서 더 좋다고 알고 있는 vsftp를 이용하여 FTP를 설정해두었다.

1. Docker Compose Configuration

외부에서도 접속이 가능할 수도 있다라는 상황을 전제하여 (평가 상에서는 어차피 Intranet만 이용하니 이를 재현하는게 꽤나 힘들어서 로컬 상에서 이용하는 경우가 허다하겠지만...) Dockerfile에 명시적으로 EXPOSE를 작성해줬고, Docker Compose 파일에서도 FTP를 이용하는 21번 포트를 21:21처럼 작성했다.
별도의 네트워크 공유는 필요 없으므로 네트워크를 설정할 필요는 없으며, Host OS에서의 접속이 가능하도록 포트 포워딩만 잊지 말자. 또한 정적 파일을 업로드하여 이를 Nginx에서 읽을 수 있도록 하는 것이 가장 큰 목적이므로, Volume도 공유하는 것을 잊지 말자.

2. vsftp Configuration

우선 vsftp의 설정 파일을 수정할 필요가 있다. 내가 필요하다고 생각해서 설정한 값들은 아래와 같다.
seccomp_sandbox : FileZilla를 이용하여 업로드 할 때 Child Died 오류를 막기 위해서 이용
anonymous_enable : 익명 사용자를 막기 위해 이용
local_enable : 로컬 상에서의 업로드를 허용하기 위해 이용
local_umask : 업로드한 파일의 기본 Permission을 설정하기 위해 (그렇지 않으면 기본 값인 400으로 설정되어 Nginx에서 읽을 때 권한 문제가 생김)
write_enable : 쓰기 작업을 허용하기 위해 이용
dirmessage_enable : 디렉토리 단위로 개별적인 메세지를 보기 위해 이용
xferlog_enable : 파일 업로드 및 다운로드의 로그를 남기기 위해 이용
xferlog_file : 파일 업로드 및 다운로드의 로그 경로를 기재하기 위해 이용
connect_from_port_20 : 20Port를 사용하지 않도록 하기 위해 이용 (FTP가 이용하는 Port2021이 있음, 21은 접속과 세션을 위한 것이고 20은 데이터 전송을 위한 것임, 파일을 FTP 서버로만 보낼 것이고 FTP에서 다른 클라이언트로 보낼 것은 아니므로 20을 사용하지 않음)
listen : xinetd가 아닌 standalone 형태로 운영할 것이므로 이를 설정하기 위해 이용 (standalone 방식 특성 상 클라이언트의 요청이 언제 들어와도 처리가 가능함, 서버 상의 메모리를 많이 이용한다는 단점이 있지만 단일 Container로 유지하기 때문에 문제가 되지 않음)
local_root : FTP 접속 후 처음으로 보여지는 경로 (직접적으로 명시하지 않고 Shell Script를 이용하여 추가함)

3. 사용자 관리

vsftp를 실제로 이용한다고 했을 때, FTP에서 내가 주로 이용하는 FileZilla를 통해 파일을 옮긴다고 가정했기 때문에 root 계정 외에 일반 사용자 계정을 추가하여 이용하도록 설정했다. (FileZillaroot 계정으로의 접속을 불허한다.) 따라서 Shell Script에서는 vsftp의 초기 설치인 경우에 환경 변수로 넘겨 받은 사용자 계정을 추가하고, 이에 대한 패스워드를 설정하도록 만들었다.
사용자 계정을 추가하는 adduser 명령어는 시스템마다 사용자의 추가적인 정보를 묻는 경우도 있기 때문에 이를 묻지 않도록 해당 정보를 명령어 상에서 채울 수 있는 —gecos 옵션을 이용했으며, 이 때 실제로 사용자의 부가 정보를 기록하는 것은 그리 좋지 않다고 판단하여 빈 문자열로 넣어 임의의 값을 유지할 수 있도록 만들었다.
패스워드의 경우에는 직접 입력을 하지 않도록 chpasswd라는 명령어를 이용했으며, 사용자 계정 이름과 더불어 패스워드도 환경 변수로 받아서 이용할수 있도록 만들었다.

4. 그룹과 소유권

사용자가 추가되었다면 바로 FTP를 이용할 수 있을 것 같지만 그렇지 않다. FTPPermission에 굉장히 민감한데, 이에 대한 설정이 없다면 파일을 업로드할 때 꽤나 골치 아픈 상황을 마주하게 된다. 따라서 파일을 업로드할 공간을 결정하고 해당 경로로만 파일을 업로드 했을 때 만큼은 문제가 없도록 설정했다.
WordPress라는 서비스를 Nginx로 제공하고 있기 떄문에 WordPress의 경로인 /var/www/wordpress를 업로드 경로로 결정했다. 따라서 chgrp, chown 명령어를 이용하여, 환경 변수로 받아온 사용자 계정을 대상으로 변경을 적용했다. 그리고 해당 경로는 실행 권한이 요구 되기 때문에 chmod까지 이용했다. (Nginx는 제공하려는 디렉토리에 대해선 실행 권한을 요구한다.)

5. vsftpd.conf의 추가 설정

위 항목까지의 설정이면 FTP를 원활히 이용할 수 있지만, 다소 불편할 수 있는 부분이 있다. FTP를 이용하여 처음으로 Connection을 만들고 나면 접속이 열리는 디렉토리가 정해져 있는데, 업로드로 이용하려는 경로를 /var/www/wordpress로만 한정했기 때문에 처음으로 보이는 경로를 바꿔주면 편할 것이다. 해당 경로 설정은 vsftpd.conf에 바로 추가하지 않았는데, 이는 /var/www/wordpress라는 경로를 환경 변수로 받으므로 Shell Script 상에서 vsfptd.conf에 추가하도록 만들었다. local_root라는 경로를 이용한다.

6. FileZilla 설치 및 실행

Virtual Box에서 아래 링크로 접속하여 FireZilla Client를 다운로드 받는다.
Download 디렉토리에 잘 다운로드 되었다면 아래 명령어를 이용하여 압축을 해제 한다.
tar xf FileZilla_3.59.0_x86_64-linux-gnu.tar.bz2
해제된 디렉토리로 들어가서 bin 디렉토리 내의 filezilla를 실행한다.
./filezilla
이전 그림처럼 libgdk-x11-2.0.so.0이 없어서 오류가 발생하는 경우엔 아래 명령어를 이용하여 패키지를 설치 후 다시 실행하면 된다.
apt-get install -y libgtk2.0-0

7. FileZilla 접속 및 파일 업로드

Nginx도 정상적으로 작동하고 있고, FTP도 설정이 정상적으로 되었으며, FileZilla 설치까지 되었으니 모든게 완벽하다. 위 그림처럼 Google 이미지를 하나 다운로드 받아서 Docker ComposeWordPress 서비스 경로로 업로드해서, 웹 서버 접속 시 해당 이미지가 정상적으로 보이는지 확인해볼 것이다.
Download 경로에는 위처럼 index.png가 잘 저장되어 있다.
도메인에 대한 설정을 이전에 다 마쳤기 때문에, 내 경우에는 jseo.42.fr로 접속이 가능하므로 이를 Host로 기재했다. 그리고 UsernamePasswordFTP 서비스를 설정할 때 인자로 넘겼던 환경 변수 값을 그대로 넣으면 되고, 이전에 vsftpd.conf에 넣을 설정 값을 설명하면서 언급 했지만 FTP 서버와 Connection 만 맺을 것이므로 Port21번으로 접속한다.
접속 후에는 위 그림의 오른쪽처럼 /var/www/wordpress 경로로 잘 접속이 되는 것을 볼 수 있으며, 왼쪽에서는 이전에 저장해뒀던 index.png도 볼 수 있다. 이제 index.png를 우클릭하여 Upload를 선택해보자.
최하단에는 Successful transfersFailed transfers가 있는데 Successful transfers에 기록이 찍힌다면 정상적으로 옮겨진 것이다. 현재 접속에 대한 로그는 디렉토리 별로 볼 수 있도록 설정했기 때문에 그림의 최상단 로그 박스를 보면 /var/www/wordpress의 로그로 파일이 잘 옮겨진 것을 확인할 수도 있다. 무엇보다도 오른쪽 부분의 index.png가 옮겨진 것을 볼 수 있으며, local_umask로 설정한 값대로 022가 잘 적용된 것을 볼 수 있다.
Virtual BoxFirefox를 이용하여 jseo.42.fr/index.png로 접속을 해보면, 문제 없이 접속되고 이미지도 잘 보이는 것을 확인할 수 있다.

3) Adminer

1. 서비스 소개

ft_server 서브젝트를 해봤다면 phpMyAdmin을 설치해봤을 것이다. phpMyAdmin이 유명해짐에 따라서 setup 폴더를 해킹하는 사례가 늘어났다는 점, 설치 시에 용량이 꽤 크다는 점 때문에 보안과 간편함에 초점을 두고 많이들 선택하는 서비스가 Adminer이다.
Adminer에서도 공식적으로 소개할 때 강조하는 부분이 보안성, 편의성, 성능, 기능 설정, 용량인 만큼 꽤 괜찮은 서비스이며, 파일 하나로만 이뤄져 있다는 점이 큰 장점으로 다가온다. 즉, Adminer에서는 데이터베이스를 생성, 삭제, 내보내기 등의 작업을 수행할 수 있는데, 이는 index.php 단 하나의 파일 만으로 수행하는 것이 가능하는 것이다.

2. Shell Script

Adminer가 동작하는 단 하나의 파일이 php 파일인 만큼, Docker Container가 생성되어 동작할 수 있도록 설정해야 하는 파일도 WordPress 때와 마찬가지로 /etc/php7/php-fpm.d/www.conf 파일이며, 이것이 동작하도록 설치해야 하는 패키지도 대체적으로 WordPress 때와 비슷하다.
Adminer는 기본 Port 번호를 8000번으로 이용하므로 이를 www.conf에 추가할 수 있도록 초기 설정에 작성하며, 처음에는 index.php 파일이 없기 때문에 이를 Adminer 공식 홈페이지에서 받아서 Container의 디렉토리에 적절히 위치시키면 된다.

3. Nginx Configuration

Adminer로 제공될 서비스는 웹으로 접속하기 때문에 해당 정적 파일을 Nginx에서 제공할 수 있도록 설정이 필요하다. adminer 라는 경로에 대한 파일 제공을 어떻게 할 것인지 작성하고, WordPress 서비스에 대한 설정과 대체적으로 비슷하게 작성하면 된다. 내 경우에는 제공하려는 스크립트를 root로 지정한 디렉토리에서 제공할 수 있도록 작성하고, 이를 Adminer 서비스의 8000번 포트로 넘겨주도록 작성했다.

4. Docker Compose Configuration

Docker Compose에서의 설정도 위의 다른 설정들과 마찬가지로 WordPressd에서 했던 것과 크게 다르지 않다. 기본적으로 데이터베이스에 의존하는 서비스이다 보니 MariaDB 서비스에 종속적이며, 이를 Nginx를 통해 정적 파일로 제공된 웹으로 볼 수 있어야 하므로 Volume 공유가 필요하다. 따라서 네트워크도 MariaDB에 접근할 수 있는 네트워크와 Nginx에 접근할 수 있는 네트워크의 설정이 필요하다.
Adminer의 연결이 완료되면 어떤 데이터베이스를 조작하고 싶은지 선택적으로 정보를 기입할 수 있기 때문에, 데이터베이스에 대한 정보는 환경 변수로 줄 필요는 없다.

4) Static Web Site

서브젝트에 명시적으로 나타난 Bonus 중 마지막 부분으로는 WordPress처럼 다른 서비스를 정적 파일로 제공할 수 있도록 구성하는 것이다. 단, WordPressphp로 빌드해서 정적 파일을 제공했었기 때문에, php가 아닌 다른 언어로 정적 파일로 제공할 수 있도록 구성해야 한다. 내 경우에는 JavaScript를 선택하여 무료로 제공해주는 React 템플릿을 이용했다. 이를 Site라는 서비스로 명명하겠다.
WordPress 때와 마찬가지로 Volume을 구성했고, /var/www/html을 사용하여 마운트 하였다. 그리고 이와 같은 서비스는 WordPress를 메인 서비스로 두고 있으므로, Reverse Proxy로 구성해뒀다. 즉, WordPress를 제공하기 위해 이용하고 있는 Nginx에서 Site로 설정한 경로로 접근하면, Site가 동작하고 있는 URIProxy Pass를 해주도록 만들었다. 말이 조금 어려워 보일 수도 있겠지만, 단순히 기존 Nginx의 설정 파일에서 location을 하나 더 작성하여 proxy_pass라는 Directive를 기재한 것이 전부이다.
위에서 언급한 것처럼 기존 Nginx로부터 접근을 받기 때문에 Site 서비스에서는 Nginx를 구성할 때 사용했던 네트워크를 적용해줘야 하고, 일반적으로 React를 이용하여 동작시키는 npm start가 아닌 정적 파일을 제공할 것이므로 Site 서비스 내에서 Nginx를 한 번 더 달아주었다. 이에 따라 ReactPort 설정을 신경 쓸 필요 없이 단순히 npm build된 파일에 대해서 Nginx가 몇 번 Port를 듣고 있을지만 명시하여 경로를 구성했다.
따라서 위 그림처럼 site 경로로 접근하게 되면 443으로 요청 받는 Nginx에서 Site 서비스로 경로를 돌려주고, Site 서비스 내에서 동작하는 Nginx에서 정적 파일을 제공해주는 형태로 잘 동작하는 것을 확인할 수 있다.

5) Grafana

서비스를 운영하다보면 반드시 필요한 요소들 중 하나가 모니터링이다. 이번 서브젝트에서 구축한 환경을 모니터링 할 수 있도록 서비스를 구성해볼 것인데, 대쉬보드인 Grafana를 먼저 진행할 것이다. 그 외에도 Influx라는 시계열 데이터베이스를 많이 이용하기도 하므로, 추가적으로 적용해볼 사람들은 시도 해보는 것도 좋겠다. 5~7번 항목에서는 Grafana ← Prometheus ← cAdvisor와 같은 구조로 구성할 것이다.

1. Docker Compose Configuration

이번 서브젝트에서는 Influx 대신에, PrometheuscAdvisor를 적용하여 이를 Grafana에서 볼 수 있도록 만들어볼 것이다. 이들을 묶어서 모니터링 서비스로 구축하기 위해선 수집한 데이터를 저장할 Volume과 해당 데이터를 이용하여 만든 웹 페이지를 제공할 수 있도록 네트워크의 연결이 필요하다. 즉, 기존의 Nginx 서비스에서 이용했던 네트워크를 모니터링 서비스에서 네트워크로 이용할 수 있도록 작성해야 하고, 특히 PrometheuscAdvisor 그리고 Grafana 끼리만 공유하는 네트워크를 추가하여 기재해야 한다.

2. Configuration

Grafana를 이용하기 위해선 4가지 설정이 필요하다. 첫 째는 Grafana 서버를 구동하기 위한 설정, 둘 째는 데이터를 제공 받기 위한 Data Source 설정, 셋 째는 데이터를 볼 수 있는 Dash Board 설정, 넷 째는 Dash Board UI 등의 설정이다.
설정을 위한 전반적인 흐름을 먼저 언급해볼 것인데, GrafanaRelease 버전을 /monitor/grafana에 설치한다고 가정해보자. 즉, 실행을 위한 bin, 설정을 위한 conf 등의 디렉토리가 /monitor/grafana 아래에 존재하게 해놨다는 가정이고, Working Directory/monitor/grafana라고 명명하겠다.
기본적으로 Grafana/monitor/grafana라는 Working Directory를 기준으로 conf 디렉토리를 찾고, 해당 디렉토리 내에서 .ini 확장자의 설정 파일을 찾도록 되어 있다. 따라서 conf 디렉토리 내에 Grafana가 읽을 설정 파일을 위치시키면 된다. 특히 Docker Compose를 구동시킬 때 Grafana-server Init Failed: Could not find config defaults, make sure homepath command line parameter is set or working directory is homepath와 같은 오류를 보게 된다면 Working Directory의 설정에 문제가 없는지 잘 살펴보아야 한다. 실행시키는 Shell Script에서 별도의 디렉토리 변경이 없거나 Dockerfile에서의 WORKDIR을 사용하지 않았다면, 위의 예시에서 기재된 Working Directory로 적절히 이동하여 /monitor/grafana에서 Grafana 서버를 실행했을 때 문제 없이 설정 파일을 읽을 수 있도록 해주면 된다. (혹은 Grafana 서버 실행 시 —config 옵션을 이용한다.)
내 경우에는 .ini의 설정 파일을 임의로 꾸몄기 때문에 Grafana의 공식 문서에 정해준 컨벤션 대로 custom.ini라는 이름을 붙였으며, 여기서는 서버 구동을 위한 설정과 Provisioning (대쉬보드 구성을 위한 제공 옵션)에 경로 설정만 해주었다. 특히 대쉬보드를 구성하기 위해선 Provisioning이라는 경로를 엄격하게 준수해야 하기 때문에 이에 대한 경로 설정이 올바르지 않으면 Grafana 접속이 원활히 되지 않으니 주의해야 한다. .ini 파일에서 설정할 수 있는 필드는 아래 링크에서 확인할 수 있다.
이제 데이터 제공을 위한 Data Source와 이를 보여주기 위한 Dash Board의 설정을 진행할 것이다. Data Source에 대한 명시는 .ini 파일에서 설정했던 Provisioning Directory를 기준으로 datasources라는 경로 내의 yml 파일에서 설정할 수 있으며, Dash Board에 대한 명시는 .ini 파일에서 설정했던 Provisioning Directory를 기준으로 dashboards라는 경로 내의 yml 파일에서 설정할 수 있다. yml 파일에서 사용할 수 있는 필드는 아래 링크에서 자세히 기술되어 있고, 예시도 적혀 있으니 필요한 필드를 뽑아서 작성해주면 되겠다. 특히 주의해야할 부분은 url이라는 필드인데, 이는 데이터를 획득하기 위한 URL을 의미하고 추후에 Prometheus가 설정이 되어야 하므로 공석으로 일단 두면 된다. 어떻게 구성해야할 지 궁금하다면 미리 찾아보거나, 방법을 안다면 미리 기재해두면 되겠다.
datasourcedashboard 항목을 CMD + F로 찾으면 금방 확인할 수 있다.
특히 Data Source에서 Prometheus를 사용하도록 만들 것이기 때문에 yml 파일 작성 시 아래 링크를 참고해보는 것도 좋다.
마지막으로 Dash Board의 전반적인 UI 구성을 위한 json 파일을 작성하면 되는데, 해당 json 파일은 Dash Boardyml 파일에서 options라는 필드의 (Docker Container 기준) path로 기재한 경로에 위치하면 된다. 그러면 Grafana 서버를 구동하면서 Dash Board를 구성하기 위해 yml 파일을 읽고, 이에 대한 UI 설정을 json 파일로 읽게 된다. json 파일에서 설정할 수 있는 필드들은 아래 링크에 나타나 있으며 이를 이용하여 적절히 구성하면 되겠다.
Grafana의 전반적인 설정 과정이 궁금하다면 아래 링크에 간단한 예시가 있으니, 위 설명을 참고하여 링크를 보면 이해가 조금 더 쉬울 것이다.

3. Environment Variables

Grafana에 접근할 수 있는 별도의 사용자를 구성하는 것이 원래는 조금 더 적합하겠으나, 이번에는 익명 사용자의 접근을 허용하는 식으로 작성하여 편의를 도모하려고 한다. Grafana는 서비스로 구동하고 있는 설정 파일 외에도 Container에서 환경 변수를 읽어서 구성되지 않은 기본 설정 값을 채우려는 시도를 하게 되는데, 이를 이용할 것이다. 내 경우에는 GF_AUTH_DISABLE_LOGIN_FORM, GF_AUTH_ANONYMOUS_ENABLED, GF_AUTH_ANONYMOUS_ORG_ROLE로 총 3개의 환경 변수를 .env에 작성하여 Docker Compose에서 사용할 수 있도록 만들었다. (Grafana 서비스로 넘겨주었다.)

4. Nginx Configuration

Grafana는 별도의 설정이 없으면 3000Port로 동작하는데, grafana라는 경로로 접근하면 이를 Grafana 서비스로 접근하도록 만들어야 한다. 따라서 Nginx 서비스에서 grafana 경로로 들어오게 되면, 이를 내부 grafana이라는 도메인의 3000PortProxy Pass를 하도록 location을 추가했다.
아래 그림처럼 정상적으로 GrafanaDash Board를 확인할 수 있다.

6) cAdvisor

Prometheus로 데이터를 모니터링 하도록 만들기 전에, 데이터를 먼저 수집해볼 것이다. cAdvisor를 간단히 소개하자면 Container Advisor의 약자이며, 이름에서 유추할 수 있듯이 Container를 위한 모니터링 툴이다. Container의 리소스 사용량과 성능 등의 정보를 제공해주며, 기본적으로 Docker를 지원한다. 이를 수집하여 무엇을 할 수 있는가에 대해서도 알아보자면, 성능 상 병목이 있는 곳을 파악하여 제거하는데 도움을 주며, 메모리가 부족한 프로세스 등을 파악하여 시스템의 확장성을 결정하는데 필요한 정보들을 얻을 수 있다.
아래는 이와 관련된 좋은 글이다.
참고로 cAdvisorContainer에 대한 정보를 제공하기 때문에 Host 자체에 대한 모니터링은 빠져 있는데, 이에 대해선 Node Exporter라는 서비스를 구성하여 얻어낼 수 있다. 당연히 구조 상 cAdvisorNode Exporter가 수집한 정보는 Prometheus로 모이도록 만들고, 이를 Grafana에서 그려낼 수 있도록 만들어 내면 된다. 여기서는 Node Exporter까지 생성하진 않는다.
아래 링크의 alice106k님의 글에 그 구조와 방법에 대해 자세히 기술되어 있으므로 참고해보자.

1. Docker Compose Configuration

Grafana로 제공할 모니터링 관련 서비스들은 별도의 네트워크를 구성했기 때문에 cAdvisor 서비스 역시 해당 네트워크를 이용하도록 만들어야 한다.
Volume의 경우엔 위 링크의 cAdvisor 공식 문서에 따라 아래와 같이 5개의 Volume을 주도록 작성했으며, Host OS의 시스템 설정에도 접근하여 이와 관련된 작업을 하는 경우에 문제가 생기지 않도록 privileged 옵션도 함께 명시했다.
/:/rootfs:ro
/var/run:/var/run:ro
/sys:/sys:ro
/var/lib/docker/:/var/lib/docker:ro
/dev/disk/:/dev/disk:ro

2. Environment Variables

cAdvisor의 기본 Port 번호는 8080번이지만, http80번과 유사한 관계로 별도의 Port 번호를 환경 변수로 넘겨서 사용했다. 또한 cAdvisor에서 수집 중인 데이터를 획득하는 과정에서 이를 접근할 수 있는 URL Base Prefix를 지정해주도록 환경 변수를 넘겨주었다. 두 값 모두 .env에서 읽어온 후, 서비스의 Container에서 읽을 수 있도록 명시했다. 이처럼 읽은 값은 Shell Script에서 cAdvisor를 실행할 때 -port 옵션과 -url_base_prefix를 통해 사용하였다.
URL Base PrefixPrometheus에서 Proxy로 접근하기 때문에 추가했는데, cAdvisor의 다양한 옵션들이 있기 때문에 아래 링크를 확인하여 명령어를 구성하면 된다.

7) Prometheus

Prometheus는 수집한 데이터를 모아서 모니터링을 수행해주는 오픈 소스 툴이라고 볼 수 있다. 5~7번까지의 설정이 잘 이해가 되지 않는다면, cAdvisorContainer의 리소스 데이터를 수집, 이를 읽어서 모니터링 하도록 만드는 것이 Prometheus, 모니터링 하고 있는 데이터를 시각적으로 구성하는 것이 Grafana라고 보면 된다.
아래 두 링크는 이들의 개념을 익히고, 설정 방법을 파악하는데 도움이 된다.

1. Docker Compose Configuration

우선 이전 항목의 Grafana에서 수집된 데이터를 읽을 수 있도록 Data Source 부분의 URL을 정상적으로 이용하기 위해서 추가해야할 것이 있다. 이전에는 해당 필드를 공석으로 뒀었는데 현재는 Prometheus라는 서비스를 생성함에 따라 이에 접근하는 것이 가능하므로 http://PROMETHEUS_HOST:PROMETHEUS_PORT라는 도메인을 이용할 수 있도록 GrafanaData Source 설정 파일에 추가할 것이다.
이름에서 볼 수 있듯 이들은 환경 변수기 때문에 적절한 HostPort.env에 추가하고, 위의 도메인처럼 작성된 URL을 정상적인 환경 변수로 이용할 수 있도록 Shell Script에서 sed 명령어를 통해 대치하였다.
그리고 Prometheus 서비스는 모니터링을 위한 것이므로 이를 위한 네트워크와 Volume을 명시하여 생성하도록 할 것이며, Prometheus에서는 cAdvisor로 접근하여 이를 읽어낼 수 있도록 cAdvisorHostPort를 환경 변수로 작성하여 Docker Compose를 구성하였다. 또한 Prometheus를 구동하기 위한 Port도 별도의 환경 변수로 넘겨받도록 작성했다.

2. Configuration

Prometheus를 구동하기 위한 설정 파일은 yml 파일 하나만 있으면 되며, yml에서 정의할 수 있는 필드들이 꽨 많은데, 내 경우에는 globalscrape만 설정해두었다. global에서는 시간 흐름과 관련된 외부에 설정된 시스템에서 확인할 때의 label 값인 external_labels와 데이터 수집 주기인 scrape_interval만 작성했다. 그리고 scrape_config에서는 Prometheus 작업과 Container의 데이터를 획득하는 cAdvisor의 작업을 명시하도록 만들었고, 각각의 수집 경로와 도메인을 명시하도록 metrics_path 그리고 static_configs 필드를 이용하였다. 이들의 작업 역시 10초 주기로 동작하도록 scrape_interval을 작성하였다.
여러 필드들의 내용은 아래 링크에서 확인할 수 있다.
특히 파일로써 Rule을 구성하여 해당 Rule의 작업을 구성하는 것도 가능한데, 이는 아래 링크를 참고하여 구성할 수 있는 것들이 있다면 해보길 권한다.

3. Nginx Configuration

Grafana의 정적 파일들은 이전에 추가했던 location 설정 덕분에 크게 문제가 없지만, Prometheus와 같이 데이터를 넘겨받아 이를 확인하기 위해 grafana/api/live 라는 경로의 설정이 필요하다. 내 경우에는 해당 경로를 이용할 때는 Prometheus를 통해 자원을 얻어내는 형태이므로, Prometheus 서비스로 넘기도록 Proxy Pass를 작성해두었다. 이와 같이 작성된 Docker Compose를 구동하면 아래 그림처럼 Grafana에서 cAdvisor를 통해 수집된 Container들의 리소스를 확인할 수 있다. 그 외도 Proxy와 관련된 여러 필드들을 설정해야 하는데 이는 Grafana에서 제공하는 공식 문서의 형식을 그대로 따랐다.