작고 통제된 환경 준비
이 절차는 systemd, 관리자 계정, 이미 설치된 Node.js 24 LTS 런타임, curl 및 Caddy의 패키지된 시스템 서비스를 갖춘 Linux 시스템을 대상으로 합니다. 먼저 설치된 버전과 패키지 경로를 확인하십시오; Node의 릴리스 페이지 에서 지원되는 릴리스 라인을 확인할 수 있습니다. 설치와 공급자 프로비저닝은 별개의 작업입니다. 자신이 제어하는 머신을 사용하고 테스트된 SSH 세션과 복구 경로를 사용할 수 있도록 유지하십시오. 다음 이름은 first-api 및 /opt/first-api 이 예시를 만들기 전에 사용되지 않은 상태여야 합니다.
HTTPS의 경우 자신이 제어하는 도메인, 올바른 A/AAAA 레코드 및 웹 트래픽을 노출할 권한도 필요합니다. api.example.com 아래는 예약된 예시입니다: 자신의 호스트 이름으로 교체하십시오. 이 API는 의도적으로 데이터베이스, 인증 또는 고객 데이터를 포함하지 않습니다. 이는 완전한 제품이나 테스트된 OffVPS 배포가 아니라 반복 가능한 프로세스를 보여줍니다.
런타임 확인 및 릴리스 하나 생성
command -v node &&
readlink -f "$(command -v node)" &&
node --version
나머지 예시는 검증된 공유 실행 파일이 다음과 같다고 가정합니다: /usr/bin/node. 다르면 모든 검사와 다음에서 해당 경로를 교체하십시오: ExecStart. 로그인 사용자의 개인 홈 안에 있는 런타임은 시스템 서비스에서 자동으로 사용할 수 없습니다. 서비스 계정과 root 소유 릴리스 디렉터리를 생성하되, 예상치 못한 기존 계정이나 경로가 발견되면 중지하십시오.
sudo useradd --system --user-group --home-dir /opt/first-api \
--shell /usr/sbin/nologin first-api &&
sudo install -d -o root -g root -m 0755 /opt/first-api/releases/001
관리 액세스 권한이 있는 편집기를 사용하여 다음을 다음 이름으로 저장하십시오: /opt/first-api/releases/001/server.mjs, root 소유이며 서비스 사용자가 읽을 수 있어야 합니다. 릴리스에는 이 파일만 포함되며 패키지 종속성이나 비밀은 없습니다.
import http from 'node:http';
const port = Number(process.env.PORT || 3000);
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
throw new Error('PORT must be an integer from 1024 to 65535');
}
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.setHeader('Cache-Control', 'no-store');
if (req.method !== 'GET') {
res.writeHead(405, { Allow: 'GET' });
res.end(JSON.stringify({ error: 'method_not_allowed' }));
return;
}
if (req.url === '/healthz') {
res.writeHead(200);
res.end(JSON.stringify({ status: 'ok', release: '001' }));
} else if (req.url === '/api/message') {
res.writeHead(200);
res.end(JSON.stringify({ message: 'A small app, running clearly.' }));
} else {
res.writeHead(404);
res.end(JSON.stringify({ error: 'not_found' }));
}
});
server.requestTimeout = 10000;
server.headersTimeout = 10000;
server.keepAliveTimeout = 5000;
server.listen(port, '127.0.0.1');
process.on('SIGTERM', () => {
server.close(() => process.exit(0));
setTimeout(() => process.exit(1), 10000).unref();
});
명시적 루프백 주소는 API 리스너를 서버 자체에 유지합니다. Caddy가 공용 진입점이 됩니다. 다음: Node HTTP API 에서 요청 처리, 타임아웃 및 서버 종료를 설명합니다. 실제로 실행할 계정으로 파일을 확인하십시오:
sudo -u first-api /usr/bin/node --check /opt/first-api/releases/001/server.mjs
프로세스에 서비스 정의 부여
저장 /etc/systemd/system/first-api.service 을 다음 내용으로:
[Unit]
Description=First API learning release
After=network.target
StartLimitIntervalSec=60
StartLimitBurst=5
[Service]
Type=simple
User=first-api
Group=first-api
WorkingDirectory=/opt/first-api/releases/001
ExecStart=/usr/bin/node /opt/first-api/releases/001/server.mjs
Environment=NODE_ENV=production
Environment=PORT=3000
Restart=on-failure
RestartSec=5
TimeoutStopSec=15
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
[Install]
WantedBy=multi-user.target
유닛은 하나의 전용 사용자, 명시적 실행 파일 및 버전이 지정된 작업 디렉터리를 사용합니다. 자동 복구는 속도가 제한되며, 의도적인 서비스 중지는 다음을 트리거하지 않습니다: Restart=on-failure. 참조: systemd.service. 파일 시스템 제한은 이 읽기 전용 API에 적합합니다; 데이터를 쓰는 애플리케이션에는 의도적으로 범위가 지정된 쓰기 가능 저장소가 필요합니다. 비밀은 이 환경 줄에 속하지 않습니다. 참조: systemd 실행 설정.
sudo systemd-analyze verify /etc/systemd/system/first-api.service &&
sudo systemctl daemon-reload &&
sudo systemctl start first-api.service &&
sudo systemctl status first-api.service --no-pager &&
curl --fail --show-error http://127.0.0.1:3000/healthz
The && 가드는 명령이 실패하면 붙여넣은 시퀀스를 중지합니다. 시작하기 전에 검증 경고를 해결하고, 검사가 실패한 후에는 다음 블록으로 계속하지 마십시오. 유닛 검증 은 구문 및 실행 파일 문제를 잡을 수 있지만 성공적인 검사가 작동하는 애플리케이션의 증명은 아닙니다. 예상 상태 응답은 다음과 같습니다: {"status":"ok","release":"001"}. 실패하면 다음을 검사하십시오: sudo journalctl -u first-api.service -n 50 --no-pager 반복적으로 재시작하기 전에.
로컬 확인 후 HTTPS 라우트 추가
사용되지 않은 파일 이름으로 기존 Caddy 구성을 백업하십시오. 다음 블록을 다음에 추가하십시오: /etc/caddy/Caddyfile 관련 없는 사이트를 교체하지 않고:
api.example.com {
reverse_proxy 127.0.0.1:3000
}
표준 공용 도메인 흐름의 경우 호스트 이름이 서버로 확인되어야 하고, 포트 80/443가 Caddy에 도달해야 하며, Caddy의 인증서 저장소는 쓰기 가능하고 영구적이어야 합니다. 게시된 모든 A/AAAA 라우트를 확인하십시오. SSH 액세스를 그대로 두고 포트 3000를 비공개로 유지하십시오. 이러한 요구 사항은 다음에서 비롯됩니다: Caddy 자동 HTTPS; 업스트림 구문은 다음에 문서화되어 있습니다: reverse_proxy.
sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile &&
sudo systemctl reload caddy.service
성공적인 검증 후에만 다시 로드하십시오; caddy validate 는 적응된 구성을 확인합니다. 패키지된 서비스 워크플로는 다음에 설명되어 있습니다: Caddy의 서비스 가이드. 별도의 클라이언트에서 실제 호스트 이름을 사용하여 다음을 요청하십시오: https://api.example.com/healthz 실제 호스트 이름을 사용하여. 인증서 검사를 우회하지 않고 신뢰할 수 있는 TLS 연결과 동일한 릴리스 응답을 기대하십시오. 그런 다음 다음을 확인하십시오: /api/message 는 그 메시지를 반환하고 알 수 없는 경로는 404을 반환합니다.
알려진 릴리스와 안전한 중지 지점 유지
두 검사가 모두 작동하면 다음을 사용하여 향후 부팅에 API를 활성화하십시오: sudo systemctl enable first-api.service. 런타임 버전, 소스 파일, 유닛 및 Caddy 구성을 기록하십시오. 다음 릴리스의 경우 새 번호가 지정된 디렉터리를 만들고, 서비스 사용자로 구문을 검사하고, 두 유닛 경로를 업데이트하고, systemd를 다시 로드하고, API를 재시작하십시오. 새 릴리스가 승인될 때까지 이전 디렉터리를 유지하십시오.
이 예시를 중지하려면 다음을 사용하십시오: sudo systemctl stop first-api.service. 해당 라우트가 구성된 동안 Caddy는 업스트림 실패를 보고합니다; 폐기할 때 이 라우트만 제거하고 Caddy를 검증/다시 로드하십시오. 실패한 코드 릴리스는 이전 유닛 경로를 선택하고 검사를 반복하여 롤백하십시오. 이후 데이터베이스 마이그레이션에는 자체 복구 계획이 필요합니다. 다음으로 계속하십시오: DNS에서 애플리케이션까지의 요청 경로 또는 앱이 중지된 이유 찾기.
사용된 문서
이 페이지의 기본 참조. 자체 환경에 설치된 버전의 문서를 확인하세요.