GitHub Actions workflow 활용 NGINX 구성 관리/배포 가이드
이 포스트는 GitHub Actions workflow 를 활용하여, GitHub 리포지토리를 통해 NGINX의 설정 파일을 관리하고 자동으로 배포하는 방법에 관해 설명합니다. GitHub 리포지토리에 push/pull request 발생 시 신규 설정 파일을 NGINX 서버에 복사하고, nginx -t 명령어를 통해 테스트 후 적용하는 방식으로 동작합니다.
이 포스트는 사전 구성된 Self-hosted runner VM을 통해서 GitHub Actions workflow를 실행합니다. Self-hosted runner 구성 방법은 GitHub Actions, Self-hosted runner 구성 가이드 포스트를 참고하세요.
목차
1. GitHub Actions workflow 사전 구성
1-1. Self-hosted runner Docker 설치
1-2. NGINX 서버 sudo 권한 설정
1-3. SSH 연결 설정
1-4. GitHub Actions secret 생성
2. GitHub Actions workflow 구성
3. GitHub Actions workflow 동작 확인
4. 결론
1. GitHub Actions workflow 사전 구성
GitHub Actions workflow를 구성하기 전에, Self-hosted runner 서버와 NGINX 서버에 사전 구성이 필요합니다.
1-1. Self-hosted runner Docker 설치
파일 전송에 사용할 scp-action을 위해, runner 서버에 Docker 설치가 필요합니다.
1. Self-hosted runner 서버에 Docker를 설치합니다.
$ sudo apt-get update
$ sudo apt install docker.io
2. 사용자를 Docker 그룹에 추가합니다.
$ sudo usermod -aG docker $(whoami)
3. 변경 사항 적용 및 Docker 권한 부여를 확인합니다.
$ newgrp docker
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
permission denied 출력 대신 위와 같은 출력을 확인합니다.
1-2. NGINX 서버 sudo 권한 설정
Self-hosted runner 서버가 NGINX 서버와 SSH 연결을 통해 workflow를 수행하기 때문에, 사용 명령어에 대한 sudo 권한 설정이 필요합니다.
1. visudo 명령어를 통해 NGINX 서버의 sudo 권한을 설정합니다.
$ sudo visudo
2. 사용자에게 비밀번호 없이 sudo 명령어를 사용할 수 있도록 파일 하단에 내용을 추가합니다.
nginx ALL=(ALL) NOPASSWD: /usr/bin/cp, /usr/bin/mkdir, /usr/bin/rm, /usr/sbin/nginx
예시의 nginx는 Self-hosted runner 서버가 ssh 접속할 NGINX 서버의 사용자입니다.
명령어의 경로는 다음과 같이 확인할 수 있습니다.
$ which rm
/usr/bin/rm
3. 비밀번호 없이 sudo 명령어 사용 가능 여부를 확인합니다.
$ sudo mkdir test
1-3. SSH 연결 설정
1. Self-hosted runner 서버에서 ed25519 키를 생성합니다.
서버가 OpenSSH를 사용하지 않을 경우 rsa 키를 생성해도 무방합니다.
$ ssh-keygen -t ed25519 -a 200 -C "github-actions"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/runner/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/runner/.ssh/id_ed25519
Your public key has been saved in /home/runner/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:pApe+ShkylDPDh5c2wMHTl1FSfT9Lqf7DDIlOrICENo github-actions
The key's randomart image is:
+--[ED25519 256]--+
| o. ..==. |
| .o .. .. . |
| o..+ . . . . |
|.oE+ * o . |
|. B.* + S . . .|
|o* *.+ . . o . |
|..+ +.. . o o o o|
| . . o . o * |
| .. ooo|
+----[SHA256]-----+
2. 생성한 SSH 키를 확인합니다.
$ ls ~/.ssh
authorized_keys id_ed25519 id_ed25519.pub
3. 공개 키(id_ed25519.pub)의 내용을 복사하여 NGINX 서버의 authorized_keys 파일에 추가합니다.
runner@runner:$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGh2Y5gQo/****** github-actions
nginx@nginx:$ ehco "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGh2Y5gQo/****** github-actions" >> ~/.ssh/authorized_keys
1-4. GitHub Actions secrets 생성
GitHub Actions workflow에 사용되는 값들을 secrets로 관리할 수 있습니다. 해당 값들은 GitHub Actions workflow의 로그에서 숨김 처리됩니다.
1. 리포지토리의 설정 메뉴로 이동합니다.

2. 좌측의 Secrets and variables 메뉴로 이동하여 새로운 secret을 추가합니다.

3. NGINX 서버의 host(IP), 사용자명(nginx), Self-hosted runner 서버의 SSH 개인 키를 추가합니다.
Secret 이름은 이후 GitHub Actions workflow 작성에 사용됩니다.
$ cat ~/.ssh/id_ed25519
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACBodmOYEKP2a+usfjT1yKqI4D5C1GUi0yAcaZIvT0WEaAAAAJhe0tFCXtLR
QgAAAAtzc2gtZWQyNTUxOQAAACBodmO*****************4D5C1GUi0yAcaZIvT0WEaA
AAAECimftigEGCItz*****************JCGRXRGD2DO2WGh2Y5gQo/Zr66x+NPXIqojg
PkLUZSLTIBxpki9PRYRoAAAADmdpdGh1Yi1hY3Rpb25zAQIDBAUGBw==
-----END OPENSSH PRIVATE KEY-----

2. GitHub Actions workflow 구성
포스트에서 사용된 GitHub 리포지토리는 다음과 같은 구조로 구성되어 있습니다.
Git-NGINX
├── README.md
├── .github
│ └── workflows
│ └── nginx.yml
└── NGINX
├── nginx.conf
└── conf.d
└── default.conf
1. 리포지토리의 Actions 메뉴로 이동하여 새 workflow를 생성합니다.


2. workflow 내용을 작성합니다.
리포지토리의 설정 파일을 NGINX 서버로 전송하고, NGINX 서버 내에서 작업을 수행하기 위해 ssh-action, scp-action을 사용했습니다.
name: Update NGINX config
# workflow가 트리거될 조건을 설정합니다.
on:
# push에 트리거
push:
# main 브랜치 설정
branches: [ "main" ]
# NGINX 설정 파일이 위치한 NGINX 디렉터리 내부 파일에 변화가 생기면 트리거 됩니다.
paths:
- 'NGINX/**'
# pull request에도 트리거
pull_request:
branches: [ "main" ]
paths:
- 'NGINX/**'
# 변수 설정. GitHub Actions secrets는 secrets.<secret 이름> 형식으로 작성합니다.
env:
SERVER_HOST: ${{ secrets.NGINX_SERVER_HOST }}
SERVER_USERNAME: ${{ secrets.NGINX_SERVER_USERNAME }}
SERVER_KEY: ${{ secrets.NGINX_SSH_PRIVATE_KEY }}
NGINX_BASE_PATH: '/etc/nginx'
TMP_BASE_PATH: '/tmp/nginx-config'
# workflow가 실행할 동작을 구성합니다
jobs:
# job 이름
deploy-nginx-config:
# Self-host runner를 통한 workflow 실행을 위해 runner의 태그 설정
runs-on: self-hosted
# job 안에서 수행할 단계 구성
steps:
# workflow가 GitHub 리포지토리를 체크하여 파일을 사용
- name: Checkout repository
uses: actions/checkout@v4
# GitHub 리포지토리의 NGINX 설정 파일을 임시 파일로 NGINX 서버로 복사(scp)
- name: Copy configuration files to temp
uses: appleboy/scp-action@v0.1.7
with:
# 변수를 사용하여 SSH 연결 설정
host: ${{ env.SERVER_HOST }}
username: ${{ env.SERVER_USERNAME }}
key: ${{ env.SERVER_KEY }}
# GitHub 리포지토리의 복사할 파일 지정
source: "NGINX/*,NGINX/conf.d/*"
# NGINX 서버로 복사할 경로 지정 - /tmp/nginx-config 경로에 임시 파일로 복사합니다.
target: ${{ env.TMP_BASE_PATH }}
# source의 상위 경로를 제거합니다. NGINX/*,NGINX/conf.d/* > *, conf.d/*
strip_components: 1
# 파일 덮어쓰기 허용
overwrite: true
# 복사한 임시 파일을 NGINX 디렉터리로 이동/테스트
- name: Deploy and Test NGINX Configuration
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ env.SERVER_HOST }}
username: ${{ env.SERVER_USERNAME }}
key: ${{ env.SERVER_KEY }}
script: |
# 백업용 디렉터리 설정 /etc/nginx/backup
BACKUP_DIR="${{ env.NGINX_BASE_PATH }}/backup"
# 백업 디렉터리 생성
echo "Creating backup..."
sudo mkdir -p "$BACKUP_DIR/conf.d"
# 기존 NGINX 설정 파일 백업
if [ -f "${{ env.NGINX_BASE_PATH }}/nginx.conf" ]; then
sudo cp "${{ env.NGINX_BASE_PATH }}/nginx.conf" "$BACKUP_DIR/nginx.conf"
fi
if [ -d "${{ env.NGINX_BASE_PATH }}/conf.d" ]; then
sudo cp -r "${{ env.NGINX_BASE_PATH }}/conf.d/"* "$BACKUP_DIR/conf.d/" 2>/dev/null || true
fi
echo "Installing new configurations..."
# 신규 NGINX 설정 파일 복사
if [ -f "${{ env.TMP_BASE_PATH }}/nginx.conf" ]; then
sudo cp "${{ env.TMP_BASE_PATH }}/nginx.conf" "${{ env.NGINX_BASE_PATH }}/nginx.conf"
fi
if [ -d "${{ env.TMP_BASE_PATH }}/conf.d" ]; then
sudo rm "${{ env.NGINX_BASE_PATH }}/conf.d/"*
sudo cp -r "${{ env.TMP_BASE_PATH }}/conf.d/"* "${{ env.NGINX_BASE_PATH }}/conf.d/"
fi
# 신규 설정 파일 테스트(nginx -t)
echo "Testing new configuration..."
# 성공 시 설정 적용
if sudo nginx -t; then
echo "Configuration test passed. Applying new configuration..."
sudo nginx -s reload
# 백업 디렉터리 삭제 단계
echo "Cleaning up backup..."
sudo rm -rf "$BACKUP_DIR"
else
# 실패 시 백업 파일로 복구
echo "Configuration test failed. Rolling back..."
if [ -f "$BACKUP_DIR/nginx.conf" ]; then
sudo cp "$BACKUP_DIR/nginx.conf" "${{ env.NGINX_BASE_PATH }}/nginx.conf"
fi
if [ -d "$BACKUP_DIR/conf.d" ]; then
sudo rm "${{ env.NGINX_BASE_PATH }}/conf.d/"*
sudo cp -r "$BACKUP_DIR/conf.d/"* "${{ env.NGINX_BASE_PATH }}/conf.d/"
fi
sudo nginx -s reload
# 백업 디렉터리 삭제
sudo rm -rf "$BACKUP_DIR"
exit 1
fi
# 임시 파일 삭제
- name: Cleanup
# step이 항상 실행되도록 설정 - 파일 복사 이후 테스트 과정에서 에러 발생 시 임시 파일 남는 현상 방지
if: always()
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ env.SERVER_HOST }}
username: ${{ env.SERVER_USERNAME }}
key: ${{ env.SERVER_KEY }}
script: |
sudo rm -rf "${{ env.TMP_BASE_PATH }}"
3. 작성이 완료되면 저장합니다.


3. GitHub Actions workflow 동작 확인
workflow의 동작을 확인하기 위해 신규 conf 파일을 commit하고 리포지토리로 push 합니다.
1. 신규 example.conf 파일을 생성합니다. 8080 포트로 listen 하는 기본 설정 파일입니다.
example.conf
server {
listen 8080;
server_name localhost;
access_log /var/log/nginx/default_access.log main;
error_log /var/log/nginx/default_error.log;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}

2. 새 파일을 commit 하고 push 합니다. 이 포스트에서는 GitHub Desktop을 사용했습니다.


3. Actions 탭에서 workflow의 실행 내용을 확인할 수 있습니다.


4. Jobs 탭에서 상세 내용 확인도 가능합니다.

5. NGINX 서버에서 명령어를 사용하여 추가된 conf 파일과, 개방된 port를 확인할 수 있습니다.
$ ls /etc/nginx/conf.d/
default.conf example.conf
$ netstat -tnlp
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 4805/nginx: worker
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 4805/nginx: worker
6. 잘못 구성된 conf 파일을 추가하고 commit, push 합니다.
test.conf
server {
listen 8080;
server_name localhost;
access_log /var/log/nginx/default_access.log main;
error_log /var/log/nginx/default_error.log;
location / {
구문 오류가 발생하는 부적절한 파일입니다.
7. Actions 탭에서 workflow의 실패 내용을 확인합니다.


설정 파일 테스트 과정에서 문제가 발생하여 workflow가 실패한 것을 확인할 수 있습니다.
해당 과정이 실패해도 임시 파일을 삭제하는 Cleanup step은 정상적으로 실행됩니다.
8. NGINX 서버에 문제가 발생한 파일이 추가되지 않고, 기존 파일만 존재하는 것을 확인할 수 있습니다.
$ ls /etc/nginx/conf.d/
default.conf example.conf
4. 결론
이번 포스트에서는 GitHub 리포지토리와 GitHub Actions의 workflow를 활용하여, NGINX 서버의 설정 파일을 관리하고 자동으로 배포하는 방법을 알아봤습니다. Self-hosted runner 서버와 NGINX 서버에서 workflow 실행에 필요한 사전 구성을 알아보고, workflow 파일을 작성하고 동작을 확인했습니다.
또한 구문 오류가 발생하는 파일을 push하면 백업한 파일을 통해 기존의 파일로 복구되는 것도 확인했습니다.
이와 같이 GitHub Actions를 활용하면 GitHub의 리포지토리를 통해 구성 파일의 버전/변경 내역 관리가 가능하며, 구문 검사 과정을 거쳐 자동으로 신규 설정을 배포할 수 있습니다.
더 나아가 다른 workflow, 브랜치를 활용하여 개발/테스트용 NGINX 서버에 신규 설정을 적용하고, 해당 서버를 통한 검증이 완료되면 main 브랜치에 통합하여 운영용 NGINX 서버에 신규 설정을 적용하는 등 다양하게 활용할 수 있을 것입니다.
GitHub Actions와 통합하여 더 효율적으로 관리 할 수 있는 NGINX Plus를 체험해 보고 싶으시다면 NGINX STORE를 통해 문의해 무료로 NGINX Plus trial을 체험해 보세요.
댓글을 달려면 로그인해야 합니다.