From 65588c339c32921fe63951007fb702cf746e1115 Mon Sep 17 00:00:00 2001 From: plpark Date: Mon, 7 Sep 2026 16:01:48 +0900 Subject: [PATCH] Add static crossword site with nginx image and k8s manifests - site/index.html: Russian literature crossword page - Dockerfile + nginx conf serving on 8080 with /healthz - k8s Deployment + NodePort Service (31380) in plpark-int-ns - Jenkinsfile: build/push to gitea registry, apply to rancher Co-Authored-By: Claude Opus 5 --- .gitignore | 7 + Dockerfile | 8 + Jenkinsfile | 68 ++++++ README.md | 45 ++++ k8s/deployment.yaml | 65 +++++ nginx/default.conf | 29 +++ site/index.html | 575 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 797 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Jenkinsfile create mode 100644 README.md create mode 100644 k8s/deployment.yaml create mode 100644 nginx/default.conf create mode 100644 site/index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eca36e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.idea/ +*.iml +*.iws + +.vscode/ + +*.log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fb7be2a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM nginx:1.27-alpine + +# 기본 서버 설정 제거 후 8080 포트용 설정 적용 +RUN rm -f /etc/nginx/conf.d/default.conf +COPY nginx/default.conf /etc/nginx/conf.d/default.conf +COPY site/ /usr/share/nginx/html/ + +EXPOSE 8080 diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..119cceb --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,68 @@ +pipeline { + agent any + + environment { + REGISTRY = 'gitea.plpark.ectech.co.kr' + IMAGE_NAME = "${REGISTRY}/plpark/crossword" + IMAGE_TAG = "${env.BUILD_NUMBER}" + NAMESPACE = 'plpark-int-ns' + } + + options { + buildDiscarder(logRotator(numToKeepStr: '20')) + } + + stages { + stage('Checkout') { + steps { + git branch: 'main', + url: 'https://gitea.plpark.ectech.co.kr/plpark/crossword.git', + credentialsId: 'gitea-registry' + } + } + + stage('Build & Push Image') { + steps { + withEnv(["PATH+DOCKER=/dockerbin"]) { + withCredentials([usernamePassword( + credentialsId: 'gitea-registry', + usernameVariable: 'REG_USER', + passwordVariable: 'REG_PASS')]) { + sh ''' + set -e + echo "$REG_PASS" | docker login "$REGISTRY" -u "$REG_USER" --password-stdin + docker build -t "$IMAGE_NAME:$IMAGE_TAG" -t "$IMAGE_NAME:latest" . + docker push "$IMAGE_NAME:$IMAGE_TAG" + docker push "$IMAGE_NAME:latest" + ''' + } + } + } + } + + stage('Deploy to Rancher') { + steps { + withEnv(["PATH+DOCKER=/dockerbin"]) { + withCredentials([file(credentialsId: 'rancher-kubeconfig', variable: 'KUBECONFIG_FILE')]) { + sh ''' + set -e + export KUBECONFIG="$KUBECONFIG_FILE" + sed "s|__IMAGE__|$IMAGE_NAME:$IMAGE_TAG|g" k8s/deployment.yaml > /tmp/crossword-deploy.yaml + kubectl apply -f /tmp/crossword-deploy.yaml + kubectl -n "$NAMESPACE" rollout status deployment/crossword --timeout=180s + ''' + } + } + } + } + } + + post { + success { + echo "Deployed ${IMAGE_NAME}:${IMAGE_TAG} -> http://192.168.0.216:31380 (https://crossword.plpark.ectech.co.kr)" + } + always { + sh 'rm -f /tmp/crossword-deploy.yaml || true' + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..e2cd101 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# crossword + +러시아 문학 가로세로 낱말풀이 — 정적 단일 페이지 사이트. + +- 공개 주소: https://crossword.plpark.ectech.co.kr +- 내부 주소: http://192.168.0.216:31380 (NodePort) + +## 구성 + +| 경로 | 설명 | +|---|---| +| `site/index.html` | 게임 본체 (원본: `프로젝트/02_crossword/russian-lit-crossword.html`) | +| `nginx/default.conf` | nginx 설정 (8080 리슨, `/healthz` 헬스체크) | +| `Dockerfile` | nginx:1.27-alpine 정적 서빙 이미지 | +| `k8s/deployment.yaml` | Deployment + NodePort Service (31380) | +| `Jenkinsfile` | Checkout → 이미지 빌드/푸시 → kubectl apply | + +Spring Boot 등 런타임은 없음. HTML/CSS/JS 만으로 동작하는 완전 정적 페이지라 +nginx 로 파일만 내려주면 끝. + +## 배포 + +Jenkins 잡(`crossword`)을 돌리면 아래가 순서대로 진행된다. + +1. gitea `plpark/crossword` main 브랜치 체크아웃 +2. `gitea.plpark.ectech.co.kr/plpark/crossword:` 빌드 & 푸시 +3. `plpark-int-ns` 네임스페이스에 apply, rollout 대기 + +Jenkins credentials 는 기존 loggen/pilot1 과 동일하게 +`gitea-registry`(usernamePassword), `rancher-kubeconfig`(file) 를 사용한다. + +## 프록시 (NPM) + +`https://npm.ectech.co.kr` 에서 Proxy Host 추가: + +- Domain Names: `crossword.plpark.ectech.co.kr` +- Scheme: `http` +- Forward Hostname / IP: `192.168.0.216` +- Forward Port: `31380` +- Block Common Exploits / Websockets Support: ON +- SSL: Let's Encrypt 발급 + Force SSL + HTTP/2 + +## 콘텐츠 수정 + +`site/index.html` 만 고치고 커밋 → Jenkins 빌드. 그 외 변경 필요 없음. diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml new file mode 100644 index 0000000..cb53267 --- /dev/null +++ b/k8s/deployment.yaml @@ -0,0 +1,65 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: crossword + namespace: plpark-int-ns + labels: + app: crossword +spec: + replicas: 1 + selector: + matchLabels: + app: crossword + template: + metadata: + labels: + app: crossword + spec: + imagePullSecrets: + - name: gitea-registry + dnsConfig: + options: + - name: ndots + value: "1" + containers: + - name: crossword + image: __IMAGE__ + ports: + - containerPort: 8080 + name: http + readinessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 10 + periodSeconds: 20 + resources: + requests: + cpu: 10m + memory: 32Mi + limits: + cpu: 200m + memory: 128Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: crossword + namespace: plpark-int-ns + labels: + app: crossword +spec: + type: NodePort + selector: + app: crossword + ports: + - name: http + port: 8080 + targetPort: 8080 + nodePort: 31380 diff --git a/nginx/default.conf b/nginx/default.conf new file mode 100644 index 0000000..890cb21 --- /dev/null +++ b/nginx/default.conf @@ -0,0 +1,29 @@ +server { + listen 8080; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + charset utf-8; + charset_types text/css application/javascript application/json; + + # 프록시(NPM) 뒤에 있으므로 실제 클라이언트 IP 로그 + real_ip_header X-Forwarded-For; + + gzip on; + gzip_min_length 1024; + gzip_types text/plain text/css application/javascript application/json image/svg+xml; + + # 단일 정적 페이지 - 어떤 경로로 들어와도 index.html + location / { + try_files $uri $uri/ /index.html; + add_header Cache-Control "no-cache, must-revalidate"; + } + + location = /healthz { + access_log off; + default_type text/plain; + return 200 "ok\n"; + } +} diff --git a/site/index.html b/site/index.html new file mode 100644 index 0000000..dab2055 --- /dev/null +++ b/site/index.html @@ -0,0 +1,575 @@ + + + + + +가로세로 낱말풀이 · 러시아 문학 + + + + + + +
+
+
Русская литература · 9 × 9
+

러시아 문학가로세로 낱말풀이

+
+
00:00경과 시간
+
0/40채운 칸
+
+
+ +
+
+
+ – + 가로 + 칸을 눌러 시작하세요. +
+ +
+
+ +
+ +
+ + + + + +
+ +

+ 같은 칸을 한 번 더 누르면 가로 ↔ 세로가 바뀌고, 오른쪽 단서도 그에 맞춰 바뀝니다.
+ 화살표 이동 · Space 방향 전환 · Tab 다음 단어 · Backspace 지우고 뒤로 +

+
+ + +
+
+ + + + + + +