NGINX Unit in Docker
Docker Container 환경에서 NGINX Unit을 사용하려면 아래와 같이 사용해야합니다.
- 컨테이너의 디렉토리에 app 파일을 마운트합니다.
- Host VM에 NGINX Unit의 포트를 오픈합니다.
예시)
$ export UNIT=$( \
docker run -d --mount type=bind,src="$(pwd)",dst=/www \
-p 8080:8000 unit:1.34.0-python3.11 \
)
이 명령은 호스트의 현재 디렉토리(앱 파일이 저장된 위치)를 컨테이너의 /www/ 디렉토리에 마운트하고, 리스너가 사용할 컨테이너의 포트 8000을 호스트의 포트 8080으로 연결합니다. 또한, 컨테이너 ID를 UNIT 환경 변수에 저장합니다.
다음으로, 제어 소켓을 통해 NGINX Unit에 설정을 업로드합니다.
$ docker exec -ti $UNIT curl -X PUT --data-binary @/www/config.json \
--unix-socket /var/run/control.unit.sock \
http://localhost/config
이 명령은 설정 파일이 호스트에 마운트된 컨테이너 디렉토리에 config.json으로 저장되어 있다고 가정합니다. 만약 해당 파일이 포트 8000에서의 리스너를 정의하고 있다면, 이제 애플리케이션은 호스트의 포트 8080에서 접근 가능합니다. Unit 설정에 대한 자세한 내용은 Configuration을 참조하세요.
| 메모
애플리케이션 컨테이너화 예제는 샘플 Go, Java, Node.js, Perl, PHP, Python, 및 Ruby Dockerfile을 참고하세요.
목차
1. NGINX Unit의 컨테이너화
2. 앱 컨테이너화
3. 2개 이상의 언어 NGINX Unit 이미지
4. Customization 시작
1. NGINX Unit의 컨테이너화
예를 들어, Flask의 공식 “Hello, World” 애플리케이션과 같은 몇 가지 종속성을 가진 웹 앱이 있다고 가정하고 진행합니다.
$ cd /path/to/app/
$ mkdir webapp
$ cat << EOF > webapp/wsgi.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
EOF
사용한 Python의 모듈을 명시했습니다.
$ cat << EOF > requirements.txt
flask
EOF
다음으로, 애플리케이션을 위한 간단한 Unit 설정을 생성합니다.
$ mkdir config
$ cat << EOF > config/config.json
{
"listeners":{
"*:8000":{
"pass":"applications/webapp"
}
},
"applications":{
"webapp":{
"type":"python 3",
"path":"/www/",
"module": "wsgi",
"callable": "app"
}
}
}
EOF
마지막으로 NGINX Unit의 상태와 로그를 저장하기 위해 log/와 state/ 디렉토리를 생성합니다.
$ mkdir log
$ touch log/unit.log
$ mkdir state
현재 file tree
/path/to/app
├── config
│ └── config.json
├── log
│ └── unit.log
├── requirements.txt
├── state
└── webapp
└── wsgi.py
Dockerfile을 생성하여 앱의 필수 구성 요소를 설치합니다.
FROM unit:1.34.0-python3.11
COPY requirements.txt /config/requirements.txt
RUN python3 -m pip install -r /config/requirements.txt
$ docker build --tag=unit-webapp .
컨테이너를 시작하여 디렉토리 구조에 마운트합니다.
$ export UNIT=$( \
docker run -d \
--mount type=bind,src="$(pwd)/config/",dst=/docker-entrypoint.d/ \
--mount type=bind,src="$(pwd)/log/unit.log",dst=/var/log/unit.log \
--mount type=bind,src="$(pwd)/state",dst=/var/lib/unit \
--mount type=bind,src="$(pwd)/webapp",dst=/www \
-p 8080:8000 unit-webapp \
)
| 메모
이 매핑을 통해 Unit은 상태와 로그를 파일 구조에 저장합니다. 기본적으로 Docker 이미지는 로그 출력을 Docker 로그 수집기 로 전달합니다 .
소스 디렉토리 config/를 컨테이너 내의 /docker-entrypoint.d/에 매핑했습니다. 공식 이미지에서는 상태가 비어 있을 경우, 해당 위치에서 발견된 모든 .json 파일을 Unit의 설정 섹션에 업로드합니다. 이제 애플리케이션을 테스트합니다.
$ curl -X GET localhost:8080
Hello, World!
애플리케이션을 디렉토리를 이동하려면, 디렉토리 구조만 이동하면 됩니다.
$ mv /path/to/app/ /new/path/to/app/
애플리케이션을 다른 NGINX Unit 이미지로 전환하려면, 해당하는 Dockerfile을 준비해야 합니다.
FROM unit:1.34.0-minimal
COPY requirements.txt /config/requirements.txt
# This time, we took a minimal Unit image to install a vanilla Python 3.9
# module, run PIP, and perform cleanup just like we did earlier.
# First, we install the required tooling and add Unit's repo.
RUN apt update && apt install -y curl apt-transport-https gnupg2 lsb-release \
&& curl -o /usr/share/keyrings/nginx-keyring.gpg \
https://unit.nginx.org/keys/nginx-keyring.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/nginx-keyring.gpg] \
https://packages.nginx.org/unit/debian/ `lsb_release -cs` unit" \
> /etc/apt/sources.list.d/unit.list
# Next, we install the module, download app requirements, and perform cleanup.
RUN apt update && apt install -y unit-python3.9 python3-pip \
&& python3 -m pip install -r /config/requirements.txt \
&& apt remove -y curl apt-transport-https gnupg2 lsb-release python3-pip \
&& apt autoremove --purge -y \
&& rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/*.list
$ docker build --tag=unit-pruned-webapp .
새 이미지를 사용하여 컨테이너를 실행하면, Unit은 자동으로 마운트 상태를 인식합니다
2. 앱 컨테이너화
개발이 완료된 Express 애플리케이션이 myapp/ 디렉토리의 app.js로 저장되어 있다고 가정해봅시다.
#!/usr/bin/env node
const http = require('http')
const express = require('express')
const app = express()
app.get('/', (req, res) => res.send('Hello, Unit!'))
http.createServer(app).listen()
같은 디렉토리에 저장된 해당 애플리케이션의 Unit 설정 파일은 config.json입니다.
{
"listeners": {
"*:8080": {
"pass": "applications/express"
}
},
"applications": {
"express": {
"type": "external",
"working_directory": "/www/",
"executable": "/usr/bin/env",
"arguments": [
"node",
"--loader",
"unit-http/loader.mjs",
"--require",
"unit-http/loader",
"app.js"
]
}
}
}
결과적으로 생성된 파일 구조는 다음과 같습니다.
myapp/
├── app.js
└── config.json
| 메모app.js 파일에 대해 chmod +x 명령을 실행하여 Unit이 해당 파일을 실행할 수 있도록 설정하는 것을 잊지 마세요.
애플리케이션을 이미지에 설치하고 구성하기 위한 Dockerfile을 준비합니다.
# Keep our base image as specific as possible.
FROM unit:1.34.0-node15
# Same as "working_directory" in config.json.
COPY myapp/app.js /www/
# Install and link Express in the app directory.
RUN cd /www && npm install express && npm link unit-http
# Port used by the listener in config.json.
EXPOSE 8080
이 이미지를 기반으로 컨테이너를 시작할 때, config.json 파일을 마운트하여 NGINX Unit의 상태를 초기화해야 합니다.
$ docker build --tag=unit-expressapp .
$ export UNIT=$( \
docker run -d \
--mount type=bind,src="$(pwd)/myapp/config.json",dst=/docker-entrypoint.d/config.json \
-p 8080:8080 unit-expressapp \
)
$ curl -X GET localhost:8080
| 참고
이 메커니즘은 NGINX Unit의 상태가 비어 있을 경우에만 컨테이너 시작 시 NGINX Unit을 초기화할 수 있게 합니다. 상태가 이미 존재하면 /docker-entrypoint.d/의 내용은 무시됩니다.
$ docker commit $UNIT unit-expressapp
$ cat << EOF > myapp/new-config.json
...
EOF
$ export UNIT=$( \
docker run -d \
--mount type=bind,src="$(pwd)/myapp/new-config.json",dst=/docker-entrypoint.d/new-config.json \
-p 8080:8080 unit-expressapp \
)
여기서, unit-expressapp 이미지를 기반으로 컨테이너를 실행할 때, Unit은 /docker-entrypoint.d/ 디렉토리에서 새로운 config.json 파일을 인식하지 않습니다. NGINX Unit의 상태가 이전에 초기화되어 저장되었기 때문입니다.
애플리케이션을 시작한 후에 설정을 구성하려면, 제어 API를 통해 파일이나 명시적인 snippet을 제공할 수 있습니다.
$ cat << EOF > myapp/new-config.json
...
EOF
$ export UNIT=$( \
docker run -d \
--mount type=bind,src="$(pwd)/myapp/new-config.json",dst=/cfg/new-config.json \
unit-expressapp \
)
$ docker exec -ti $UNIT curl -X PUT --data-binary @/cfg/new-config.json \
--unix-socket /var/run/control.unit.sock \
http://localhost/config
$ docker exec -ti $UNIT curl -X PUT -d '"/www/newapp/"' \
--unix-socket /var/run/control.unit.sock \
http://localhost/config/applications/express/working_directory
이 접근 방식은 외부 종속성을 가진 모든 NGINX Unit 지원 애플리케이션에 적용 가능합니다.
3. 2개 이상의 언어 NGINX Unit 이미지
이전에 Unit은 모든 지원 언어의 모듈이 포함된 -full Docker 이미지를 제공했으나, 버전 1.22.0부터 중단되었습니다. 여전히 다중 언어 이미지를 사용해야 하는 경우, 다음과 같은 Dockerfile 템플릿을 사용하세요. 이 템플릿은 Debian 11 기반의 최소 Unit 이미지로 시작하여 공식 언어 모듈 패키지를 설치합니다.
FROM unit:1.34.0-minimal
# We take a minimal Unit image and install language-specific modules.
# First, we install the required tooling and add Unit's repo.
RUN apt update && apt install -y curl apt-transport-https gnupg2 lsb-release \
&& curl -o /usr/share/keyrings/nginx-keyring.gpg \
https://unit.nginx.org/keys/nginx-keyring.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/nginx-keyring.gpg] \
https://packages.nginx.org/unit/debian/ `lsb_release -cs` unit" \
> /etc/apt/sources.list.d/unit.list
# Next, we install the necessary language module packages and perform cleanup.
RUN apt update && apt install -y \
unit-jsc11 unit-perl unit-php unit-python2.7 unit-python3.9 unit-ruby \
&& apt remove -y curl apt-transport-https gnupg2 lsb-release \
&& apt autoremove --purge -y \
&& rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/*.list
패키지 대신 사용자 정의 모듈을빌드할 수도 있습니다. 이를 위해 Dockerfile.* 템플릿을 참고하세요.
4. Customization 시작
새로운 Dockerfile 레이어를 추가하여 컨테이너에서 Unit이 시작되는 방식을 커스터마이즈할 수 있습니다.
FROM unit:1.34.0-minimal
CMD ["unitd-debug","--no-daemon","--control","unix:/var/run/control.unit.sock"]
위의 CMD 명령은 기본 unitd 실행 파일을 디버그 버전으로 교체합니다. NGINX Unit의 명령줄 옵션을 사용하여 시작 동작을 변경할 수 있습니다.
FROM unit:1.34.0-minimal
CMD ["unitd","--no-daemon","--control","0.0.0.0:8080"]
명령은 NGINX Unit의 기본 UNIX 도메인 제어 소켓을 IP 소켓 주소로 변경합니다.
NGINX Unit의 더 많은 정보를 알고싶으시다면 NGINX STORE NGINX Unit을 방문해주세요.