Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions .github/workflows/dev-ci-cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
name: Dev CI/CD

on:
pull_request:
branches:
- develop
push:
branches:
- develop
workflow_dispatch:

concurrency:
group: dev-cicd-${{ github.ref }}
cancel-in-progress: true

permissions:
{}

env:
JAVA_VERSION: "21"

jobs:
test:
runs-on: ubuntu-24.04
permissions:
contents: read

steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

- name: Set up JDK
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: temurin
java-version: ${{ env.JAVA_VERSION }}

- name: Set up Gradle
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0

- name: Run tests
run: ./gradlew test

publish-dev-image:
if: github.event_name != 'pull_request'
needs: test
runs-on: ubuntu-24.04
permissions:
contents: write
packages: write

steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0

- name: Set up Kustomize
run: |
KUSTOMIZE_VERSION=5.7.1
curl -sSL -o /tmp/kustomize.tar.gz \
"https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv${KUSTOMIZE_VERSION}/kustomize_v${KUSTOMIZE_VERSION}_linux_amd64.tar.gz"
tar -xzf /tmp/kustomize.tar.gz -C /tmp
sudo mv /tmp/kustomize /usr/local/bin/kustomize
kustomize version

- name: Normalize image name
run: |
echo "IMAGE_NAME=ghcr.io/$(echo "${GITHUB_REPOSITORY_OWNER}" | tr '[:upper:]' '[:lower:]')/project-auth-server" >> "$GITHUB_ENV"
echo "IMAGE_TAG=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"

- name: Login to GHCR
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push dev image
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
with:
context: .
file: ./Dockerfile
push: true
tags: |
${{ env.IMAGE_NAME }}:dev
${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}

- name: Update dev image tag for Argo CD
run: |
cd k8s/dev
kustomize edit set image "${IMAGE_NAME}=${IMAGE_NAME}:${IMAGE_TAG}"

- name: Commit and push manifest update
run: |
if git diff --quiet -- k8s/dev/kustomization.yaml; then
echo "No manifest change detected"
exit 0
fi

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add k8s/dev/kustomization.yaml
git commit -m "chore: update auth-server dev image to ${IMAGE_TAG}"
git push
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,74 @@ Actuator health endpoint는 아래 경로를 사용합니다.
- `/livez`
- `/readyz`

## Dev CI/CD

이 저장소는 [`.github/workflows/dev-ci-cd.yml`](/home/donghyeon/dev/Project-Auth-Server/.github/workflows/dev-ci-cd.yml) 기준으로 dev CI/CD를 구성합니다.

- Pull Request to `develop`
- `./gradlew test`
- Push to `develop`
- `./gradlew test`
- `ghcr.io/<owner>/project-auth-server:dev`
- `ghcr.io/<owner>/project-auth-server:<short-sha>`
두 태그로 이미지를 빌드/푸시
- `k8s/dev/kustomization.yaml`의 `newTag`를 새 `<short-sha>`로 갱신
- 같은 `develop` 브랜치에 manifest 변경 커밋 반영
- Argo CD가 `develop`을 감시 중이면 새 태그를 sync

현재 dev 배포 선언은 아래 파일로 관리합니다.

- Kustomize: [k8s/dev/kustomization.yaml](/home/donghyeon/dev/Project-Auth-Server/k8s/dev/kustomization.yaml)
- Argo CD AppProject: [argocd/auth-dev-project.yaml](/home/donghyeon/dev/Project-Auth-Server/argocd/auth-dev-project.yaml)
- Argo CD Application: [argocd/dev-auth-server-application.yaml](/home/donghyeon/dev/Project-Auth-Server/argocd/dev-auth-server-application.yaml)

민감값은 Git에 직접 올리지 않고, [k8s/dev/secret.yaml](/home/donghyeon/dev/Project-Auth-Server/k8s/dev/secret.yaml)에 키 구조만 유지한 채 placeholder 값만 둡니다.
현재 dev 구성은 secret까지 Argo CD가 직접 생성하는 방식이 아니라, 실제 secret은 namespace에 사전 생성하고 Argo CD는 그 참조만 배포하는 방식입니다.

현재 workflow는 아래 기준으로 정리되어 있습니다.

- runner: `ubuntu-24.04`
- major tag 대신 명시적 action version 사용
- 현재 단일 아키 빌드만 하므로 QEMU 제거
- 배포 기준 태그는 `dev`가 아니라 `<short-sha>`
- `dev`는 편의용 moving tag로만 유지

dev namespace에서 먼저 필요한 secret은 아래 두 개입니다.

1. GHCR pull secret

```bash
kubectl create secret docker-registry ghcr-regcred \
--namespace auth-dev \
--docker-server=ghcr.io \
--docker-username=<github-username> \
--docker-password=<github-pat-or-ghcr-token>
```

2. auth-server secret

```bash
kubectl apply -f k8s/dev/namespace.yaml
kubectl apply -f k8s/dev/serviceaccount.yaml
kubectl apply -f k8s/dev/service.yaml
kubectl apply -f k8s/dev/configmap.yaml

cp k8s/dev/secret.yaml /tmp/auth-server-secret.yaml
# /tmp/auth-server-secret.yaml 안의 change-me 값을 실제 값으로 교체
kubectl apply -f /tmp/auth-server-secret.yaml
```

Argo CD는 클러스터에 별도 설치해야 합니다. 이 저장소는 Argo CD가 읽을 `Application` 선언만 함께 관리합니다.
적용 순서는 보통 `AppProject -> Application` 순서로 가져갑니다.

현재 dev namespace는 Pod Security Admission 기준으로 아래 라벨을 사용합니다.

- `enforce=baseline`
- `warn=restricted`
- `audit=restricted`

Deployment는 이에 맞춰 `runAsNonRoot`, `seccompProfile: RuntimeDefault`, `allowPrivilegeEscalation: false`, `capabilities.drop: [ALL]`, `startupProbe`, `livenessProbe`, `readinessProbe`를 포함합니다.

## Flyway 운영 기준

Flyway는 스키마 변경을 추적하기 위해 이번 브랜치에서 적용했습니다. 다만 운영 환경에서 애플리케이션 Pod가 스케일 아웃될 때마다 마이그레이션을 시도하게 두는 구조는 지양합니다.
Expand Down
48 changes: 48 additions & 0 deletions argocd/auth-dev-project.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: auth-dev
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io

spec:
description: Project Auth Server dev deployment project

sourceRepos:
- https://github.com/DongHyeonka/Project-Auth-Server.git

destinations:
- namespace: auth-dev
server: https://kubernetes.default.svc

clusterResourceWhitelist:
- group: ""
kind: Namespace

namespaceResourceWhitelist:
- group: ""
kind: ConfigMap
- group: ""
kind: Secret
- group: ""
kind: Service
- group: ""
kind: ServiceAccount
- group: "apps"
kind: Deployment
- group: "apps"
kind: StatefulSet
- group: "apps"
kind: ReplicaSet
- group: "autoscaling"
kind: HorizontalPodAutoscaler
- group: "batch"
kind: Job
- group: "networking.k8s.io"
kind: Ingress
- group: "policy"
kind: PodDisruptionBudget

orphanedResources:
warn: true
36 changes: 36 additions & 0 deletions argocd/dev-auth-server-application.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: auth-server-dev
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: auth-dev

source:
repoURL: https://github.com/DongHyeonka/Project-Auth-Server.git
targetRevision: develop
path: k8s/dev

destination:
server: https://kubernetes.default.svc
namespace: auth-dev

syncPolicy:
automated:
enabled: true
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- PruneLast=true
- ApplyOutOfSyncOnly=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m

revisionHistoryLimit: 5
25 changes: 25 additions & 0 deletions k8s/dev/configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: auth-server-config
data:
SPRING_PROFILES_ACTIVE: dev
APP_DOCS_TITLE: Project Auth Server API
APP_DOCS_DESCRIPTION: dev auth-server OpenAPI
APP_DOCS_VERSION: v1
APP_DATASOURCE_URL: jdbc:postgresql://postgres.platform.svc.cluster.local:5432/project_auth
APP_PERSISTENCE_MIGRATION_RUN_ON_STARTUP: "false"
APP_SECURITY_OAUTH2_KEYCLOAK_ISSUER_URI: http://keycloak.platform.svc.cluster.local:8080/realms/project-auth
APP_SECURITY_OAUTH2_KEYCLOAK_CLIENT_ID: project-auth-server
APP_SECURITY_OAUTH2_GOOGLE_REGISTRATION_ID: keycloak-google
APP_SECURITY_OAUTH2_GOOGLE_IDP_HINT: google
APP_SECURITY_OAUTH2_GITHUB_REGISTRATION_ID: keycloak-github
APP_SECURITY_OAUTH2_GITHUB_IDP_HINT: github
APP_SECURITY_JWT_ISSUER: http://auth-server.auth-dev.svc.cluster.local:8080
APP_SECURITY_JWT_ACTIVE_KEY_ID: dev-vault-rsa-1
APP_SECURITY_JWT_GENERATE_KEY_PAIR_ON_STARTUP: "false"
APP_SECURITY_JWT_ACCESS_TOKEN_EXPIRATION: PT30M
APP_SECURITY_JWT_VAULT_ENABLED: "true"
APP_SECURITY_JWT_VAULT_ADDRESS: http://vault.platform.svc.cluster.local:8200
APP_SECURITY_JWT_VAULT_MOUNT_PATH: transit
APP_SECURITY_JWT_VAULT_TRANSIT_KEY_NAME: project-auth-jwt
72 changes: 72 additions & 0 deletions k8s/dev/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: auth-server

spec:
replicas: 1
selector:
matchLabels:
app: auth-server

template:
metadata:
labels:
app: auth-server

spec:
serviceAccountName: auth-server
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: auth-server
image: ghcr.io/donghyeonka/project-auth-server
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: http
envFrom:
- configMapRef:
name: auth-server-config
- secretRef:
name: auth-server-secret
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 1000m
memory: 1024Mi
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: http
initialDelaySeconds: 20
timeoutSeconds: 3
failureThreshold: 3
periodSeconds: 10
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: http
initialDelaySeconds: 30
timeoutSeconds: 3
failureThreshold: 5
periodSeconds: 15
startupProbe:
httpGet:
path: /actuator/health/liveness
port: http
initialDelaySeconds: 10
timeoutSeconds: 3
periodSeconds: 10
failureThreshold: 12
15 changes: 15 additions & 0 deletions k8s/dev/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: auth-dev

resources:
- namespace.yaml
- serviceaccount.yaml
- configmap.yaml
- service.yaml
- deployment.yaml

images:
- name: ghcr.io/donghyeonka/project-auth-server
newTag: dev
11 changes: 11 additions & 0 deletions k8s/dev/namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: v1
kind: Namespace
metadata:
name: auth-dev
labels:
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: latest
Loading