diff --git a/.github/workflows/terraform-apply.yml b/.github/workflows/terraform-apply.yml index 73a4c8e..e4f2358 100644 --- a/.github/workflows/terraform-apply.yml +++ b/.github/workflows/terraform-apply.yml @@ -4,14 +4,14 @@ name: PinHouse Terraform Apply 파이프라인 on: workflow_dispatch: inputs: - environment: - description: "적용할 환경을 선택합니다. dev, staging, prod 중 하나를 사용합니다." + target: + description: "적용할 대상을 선택합니다. all을 선택하면 dev와 prod를 병렬로 적용합니다." required: true type: choice options: - dev - - staging - prod + - all confirm: description: '"apply"를 입력해야 실제 적용이 진행됩니다.' required: true @@ -22,18 +22,50 @@ permissions: contents: read id-token: write -env: - GCP_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} - GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} - TERRAFORM_STATE_BUCKET: ${{ secrets.TERRAFORM_STATE_BUCKET }} - TERRAFORM_TFVARS_DEV: ${{ secrets.TERRAFORM_TFVARS_DEV }} - TERRAFORM_TFVARS_PROD: ${{ secrets.TERRAFORM_TFVARS_PROD }} - jobs: + prepare-matrix: + name: 적용 대상 준비 + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.prepare.outputs.matrix }} + + steps: + - name: 적용 대상 Matrix 생성 + id: prepare + run: | + case "${{ github.event.inputs.target }}" in + dev) + MATRIX='[{"terraform_environment":"dev","github_environment":"PinHouse_dev"}]' + ;; + prod) + MATRIX='[{"terraform_environment":"prod","github_environment":"PinHouse_prod"}]' + ;; + all) + MATRIX='[{"terraform_environment":"dev","github_environment":"PinHouse_dev"},{"terraform_environment":"prod","github_environment":"PinHouse_prod"}]' + ;; + *) + echo "오류: 지원하지 않는 대상입니다: ${{ github.event.inputs.target }}" + exit 1 + ;; + esac + + echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT" + terraform-apply: - name: Apply - ${{ github.event.inputs.environment }} + name: Apply - ${{ matrix.terraform_environment }} runs-on: ubuntu-latest - environment: ${{ github.event.inputs.environment }} + needs: prepare-matrix + if: needs.prepare-matrix.outputs.matrix != '' + strategy: + fail-fast: false + matrix: + include: ${{ fromJson(needs.prepare-matrix.outputs.matrix) }} + environment: ${{ matrix.github_environment }} + env: + GCP_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} + TERRAFORM_STATE_BUCKET: ${{ secrets.TERRAFORM_STATE_BUCKET }} + TERRAFORM_TFVARS: ${{ secrets.TERRAFORM_TFVARS }} steps: - name: 적용 확인 값 검증 @@ -63,44 +95,33 @@ jobs: - name: State 버킷 변수 확인 run: | if [ -z "${{ env.TERRAFORM_STATE_BUCKET }}" ]; then - echo "오류: GitHub Secrets에 TERRAFORM_STATE_BUCKET을 설정해야 합니다." + echo "오류: GitHub Environment Secret TERRAFORM_STATE_BUCKET을 설정해야 합니다." exit 1 fi - name: Terraform 초기화 id: init - working-directory: terraform/environments/${{ github.event.inputs.environment }} + working-directory: terraform/environments/${{ matrix.terraform_environment }} run: | terraform init \ -backend-config="bucket=${{ env.TERRAFORM_STATE_BUCKET }}" \ - -backend-config="prefix=terraform/${{ github.event.inputs.environment }}/state" + -backend-config="prefix=terraform/${{ matrix.terraform_environment }}/state" - name: Terraform tfvars 복원 id: tfvars - working-directory: terraform/environments/${{ github.event.inputs.environment }} + working-directory: terraform/environments/${{ matrix.terraform_environment }} run: | - case "${{ github.event.inputs.environment }}" in - prod) - TFVARS_CONTENT="${TERRAFORM_TFVARS_PROD}" - SECRET_NAME="TERRAFORM_TFVARS_PROD" - ;; - *) - echo "오류: 지원하지 않는 환경입니다: ${{ github.event.inputs.environment }}" - exit 1 - ;; - esac - - if [ -z "${TFVARS_CONTENT}" ]; then - echo "오류: GitHub Secret ${SECRET_NAME}를 설정해야 합니다." + if [ -z "${TERRAFORM_TFVARS}" ]; then + echo "오류: GitHub Environment Secret TERRAFORM_TFVARS를 설정해야 합니다." exit 1 fi - printf '%s\n' "${TFVARS_CONTENT}" > terraform.tfvars + printf '%s\n' "${TERRAFORM_TFVARS}" > terraform.tfvars chmod 600 terraform.tfvars - name: Terraform Plan 실행 id: plan - working-directory: terraform/environments/${{ github.event.inputs.environment }} + working-directory: terraform/environments/${{ matrix.terraform_environment }} run: | terraform plan -no-color -out=tfplan terraform show -no-color tfplan > plan_output.txt @@ -108,14 +129,14 @@ jobs: - name: Plan 결과 업로드 uses: actions/upload-artifact@v4 with: - name: tfplan-${{ github.event.inputs.environment }}-${{ github.run_number }} + name: tfplan-${{ matrix.terraform_environment }}-${{ github.run_number }} path: | - terraform/environments/${{ github.event.inputs.environment }}/tfplan - terraform/environments/${{ github.event.inputs.environment }}/plan_output.txt + terraform/environments/${{ matrix.terraform_environment }}/tfplan + terraform/environments/${{ matrix.terraform_environment }}/plan_output.txt - name: Terraform Apply 실행 id: apply - working-directory: terraform/environments/${{ github.event.inputs.environment }} + working-directory: terraform/environments/${{ matrix.terraform_environment }} run: | terraform apply -auto-approve tfplan 2>&1 | tee apply_output.txt @@ -123,25 +144,26 @@ jobs: uses: actions/upload-artifact@v4 if: always() with: - name: tfapply-${{ github.event.inputs.environment }}-${{ github.run_number }} - path: terraform/environments/${{ github.event.inputs.environment }}/apply_output.txt + name: tfapply-${{ matrix.terraform_environment }}-${{ github.run_number }} + path: terraform/environments/${{ matrix.terraform_environment }}/apply_output.txt - name: 배포 요약 생성 if: always() run: | - echo "## Terraform Apply 요약 - ${{ github.event.inputs.environment }}" >> $GITHUB_STEP_SUMMARY + echo "## Terraform Apply 요약 - ${{ matrix.terraform_environment }}" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "**환경:** ${{ github.event.inputs.environment }}" >> $GITHUB_STEP_SUMMARY + echo "**환경:** ${{ matrix.terraform_environment }}" >> $GITHUB_STEP_SUMMARY + echo "**GitHub Environment:** ${{ matrix.github_environment }}" >> $GITHUB_STEP_SUMMARY echo "**실행 결과:** ${{ steps.apply.outcome }}" >> $GITHUB_STEP_SUMMARY echo "**실행 사용자:** @${{ github.actor }}" >> $GITHUB_STEP_SUMMARY echo "**커밋 SHA:** ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - if [ -f terraform/environments/${{ github.event.inputs.environment }}/apply_output.txt ]; then + if [ -f terraform/environments/${{ matrix.terraform_environment }}/apply_output.txt ]; then echo "
Apply 출력 보기" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY - cat terraform/environments/${{ github.event.inputs.environment }}/apply_output.txt >> $GITHUB_STEP_SUMMARY + cat terraform/environments/${{ matrix.terraform_environment }}/apply_output.txt >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY echo "
" >> $GITHUB_STEP_SUMMARY fi @@ -149,5 +171,5 @@ jobs: - name: 실패 시 워크플로우 종료 if: failure() run: | - echo "::error::${{ github.event.inputs.environment }} 환경 Terraform apply 실행에 실패했습니다." + echo "::error::${{ matrix.terraform_environment }} 환경 Terraform apply 실행에 실패했습니다." exit 1 diff --git a/.github/workflows/terraform-plan.yml b/.github/workflows/terraform-plan.yml index 9114cf6..ed7d5b5 100644 --- a/.github/workflows/terraform-plan.yml +++ b/.github/workflows/terraform-plan.yml @@ -24,6 +24,7 @@ jobs: GCP_WORKLOAD_IDENTITY_PROVIDER: "" GCP_SERVICE_ACCOUNT: "" TERRAFORM_STATE_BUCKET: "" + TERRAFORM_TFVARS_DEV: "" TERRAFORM_TFVARS_PROD: "" outputs: environments: ${{ steps.detect.outputs.environments || '[]' }} @@ -45,11 +46,16 @@ jobs: # 변경된 환경 목록을 배열로 수집합니다. ENVIRONMENTS=() + if echo "$CHANGED_FILES" | grep -q "terraform/environments/dev/"; then + ENVIRONMENTS+=("dev") + fi + if echo "$CHANGED_FILES" | grep -q "terraform/environments/prod/"; then ENVIRONMENTS+=("prod") fi if echo "$CHANGED_FILES" | grep -q "terraform/modules/"; then + ENVIRONMENTS+=("dev") ENVIRONMENTS+=("prod") fi @@ -73,6 +79,7 @@ jobs: GCP_WORKLOAD_IDENTITY_PROVIDER: "" GCP_SERVICE_ACCOUNT: "" TERRAFORM_STATE_BUCKET: "" + TERRAFORM_TFVARS_DEV: "" TERRAFORM_TFVARS_PROD: "" steps: @@ -99,11 +106,12 @@ jobs: runs-on: ubuntu-22.04 needs: detect-changes if: (needs.detect-changes.outputs.environments || '[]') != '[]' # 변경된 환경이 있을 때만 실행합니다. + environment: ${{ matrix.environment == 'dev' && 'PinHouse_dev' || matrix.environment == 'prod' && 'PinHouse_prod' }} env: GCP_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} TERRAFORM_STATE_BUCKET: ${{ secrets.TERRAFORM_STATE_BUCKET }} - TERRAFORM_TFVARS_PROD: ${{ secrets.TERRAFORM_TFVARS_PROD }} + TERRAFORM_TFVARS: ${{ secrets.TERRAFORM_TFVARS }} strategy: fail-fast: false # 한 환경 실패가 다른 환경 확인을 막지 않도록 유지합니다. matrix: @@ -140,7 +148,7 @@ jobs: id: state-bucket run: | if [ -z "${{ env.TERRAFORM_STATE_BUCKET }}" ]; then - echo "오류: GitHub Secrets에 TERRAFORM_STATE_BUCKET을 설정해야 합니다." + echo "오류: GitHub Environment Secret TERRAFORM_STATE_BUCKET을 설정해야 합니다." exit 1 fi @@ -158,15 +166,12 @@ jobs: if: steps.init.outcome == 'success' working-directory: terraform/environments/${{ matrix.environment }} run: | - TFVARS_CONTENT="${TERRAFORM_TFVARS_PROD}" - SECRET_NAME="TERRAFORM_TFVARS_PROD" - - if [ -z "${TFVARS_CONTENT}" ]; then - echo "오류: GitHub Secret ${SECRET_NAME}를 설정해야 합니다." + if [ -z "${TERRAFORM_TFVARS}" ]; then + echo "오류: GitHub Environment Secret TERRAFORM_TFVARS를 설정해야 합니다." exit 1 fi - printf '%s\n' "${TFVARS_CONTENT}" > terraform.tfvars + printf '%s\n' "${TERRAFORM_TFVARS}" > terraform.tfvars chmod 600 terraform.tfvars - name: Terraform 유효성 검사 diff --git a/k8s-argocd/applications/dev/app.yaml b/k8s-argocd/applications/dev/app.yaml new file mode 100644 index 0000000..071252a --- /dev/null +++ b/k8s-argocd/applications/dev/app.yaml @@ -0,0 +1,41 @@ +# =================================== +# Dev App Root +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: app-root-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: app + finalizers: + - resources-finalizer.argocd.argoproj.io + +# 스펙 +spec: + project: default + + # Git 저장소에서 관리하는 매니페스트 경로입니다. + source: + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + path: k8s-argocd/applications/dev/app + directory: + recurse: false + + # 배포 대상 클러스터와 네임스페이스입니다. + destination: + server: https://kubernetes.default.svc + namespace: argocd + + # Git 기준으로 자동 동기화합니다. + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s-argocd/applications/dev/app/backend.yaml b/k8s-argocd/applications/dev/app/backend.yaml new file mode 100644 index 0000000..08c1965 --- /dev/null +++ b/k8s-argocd/applications/dev/app/backend.yaml @@ -0,0 +1,50 @@ +# =================================== +# Dev Backend +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: backend-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/image-updater: enabled + finalizers: + - resources-finalizer.argocd.argoproj.io + annotations: + argocd-image-updater.argoproj.io/image-list: asia-northeast3-docker.pkg.dev/dev-pinhouse/pinhouse-dev-be/pinhouse-server + argocd-image-updater.argoproj.io/backend.update-strategy: newest-build + argocd-image-updater.argoproj.io/backend.allow-tags: regexp:^[0-9]{8}_[0-9]{6}-[a-f0-9]{7}$ + argocd-image-updater.argoproj.io/backend.kustomize.image-name: REPLACE_ME + argocd-image-updater.argoproj.io/write-back-method: git + argocd-image-updater.argoproj.io/git-branch: main + + notifications.argoproj.io/subscribe.on-sync-running.backend-nonprod: "" + notifications.argoproj.io/subscribe.on-deployed.backend-nonprod: "" + notifications.argoproj.io/subscribe.on-sync-failed.backend-nonprod: "" + notifications.argoproj.io/subscribe.on-health-degraded.backend-nonprod: "" + +spec: + project: default + + # Git 저장소에서 관리하는 매니페스트 경로입니다. + source: + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + path: k8s-kustomize/overlays/dev/backend + + # 배포 대상 클러스터와 네임스페이스입니다. + destination: + server: https://kubernetes.default.svc + namespace: dev-app + + # Git 기준으로 자동 동기화합니다. + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s-argocd/applications/dev/app/frontend.yaml b/k8s-argocd/applications/dev/app/frontend.yaml new file mode 100644 index 0000000..ae58cd7 --- /dev/null +++ b/k8s-argocd/applications/dev/app/frontend.yaml @@ -0,0 +1,49 @@ +# =================================== +# Dev Frontend +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: frontend-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/image-updater: enabled + finalizers: + - resources-finalizer.argocd.argoproj.io + annotations: + argocd-image-updater.argoproj.io/image-list: asia-northeast3-docker.pkg.dev/dev-pinhouse/pinhouse-dev-fe/pinhouse-web + argocd-image-updater.argoproj.io/frontend.update-strategy: newest-build + argocd-image-updater.argoproj.io/frontend.allow-tags: regexp:^[0-9]{8}_[0-9]{6}-[a-f0-9]{7}$ + argocd-image-updater.argoproj.io/frontend.kustomize.image-name: REPLACE_ME + argocd-image-updater.argoproj.io/write-back-method: git + argocd-image-updater.argoproj.io/git-branch: main + + notifications.argoproj.io/subscribe.on-deployed.frontend-nonprod: "" + notifications.argoproj.io/subscribe.on-sync-failed.frontend-nonprod: "" + notifications.argoproj.io/subscribe.on-health-degraded.frontend-nonprod: "" + +spec: + project: default + + # Git 저장소에서 관리하는 매니페스트 경로입니다. + source: + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + path: k8s-kustomize/overlays/dev/frontend + + # 배포 대상 클러스터와 네임스페이스입니다. + destination: + server: https://kubernetes.default.svc + namespace: dev-app + + # Git 기준으로 자동 동기화합니다. + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s-argocd/applications/dev/app/image-updater.yaml b/k8s-argocd/applications/dev/app/image-updater.yaml new file mode 100644 index 0000000..9e5261b --- /dev/null +++ b/k8s-argocd/applications/dev/app/image-updater.yaml @@ -0,0 +1,21 @@ +# =================================== +# Image Updator +# =================================== + +apiVersion: argocd-image-updater.argoproj.io/v1alpha1 +kind: ImageUpdater + +# 기본 정보 +metadata: + name: dev-applications + namespace: argocd + +spec: + # 라벨을 기준으로 Image Updater 적용 대상을 찾습니다. + applicationRefs: + - namePattern: "*" + labelSelectors: + matchLabels: + pinhouse.co.kr/image-updater: enabled + pinhouse.co.kr/environment: dev + useAnnotations: true diff --git a/k8s-argocd/applications/dev/monitoring.yaml b/k8s-argocd/applications/dev/monitoring.yaml new file mode 100644 index 0000000..c1cc55a --- /dev/null +++ b/k8s-argocd/applications/dev/monitoring.yaml @@ -0,0 +1,41 @@ +# =================================== +# Dev Monitoring Root +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: monitoring-root-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: monitoring + finalizers: + - resources-finalizer.argocd.argoproj.io + +# 스펙 +spec: + project: default + + # Git 저장소에서 관리하는 매니페스트 경로입니다. + source: + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + path: k8s-argocd/applications/dev/monitoring + directory: + recurse: false + + # 배포 대상 클러스터와 네임스페이스입니다. + destination: + server: https://kubernetes.default.svc + namespace: argocd + + # Git 기준으로 자동 동기화합니다. + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s-argocd/applications/dev/monitoring/monitoring-alloy.yaml b/k8s-argocd/applications/dev/monitoring/monitoring-alloy.yaml new file mode 100644 index 0000000..0779ec0 --- /dev/null +++ b/k8s-argocd/applications/dev/monitoring/monitoring-alloy.yaml @@ -0,0 +1,45 @@ +# =================================== +# Dev Monitoring Alloy +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: monitoring-alloy-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: monitoring + pinhouse.co.kr/monitoring-component: alloy + annotations: + argocd.argoproj.io/sync-wave: "2" + finalizers: + - resources-finalizer.argocd.argoproj.io + +spec: + project: default + + # Git 저장소에서 관리하는 매니페스트 경로입니다. + source: + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + path: k8s-helm/releases/monitoring-alloy + helm: + releaseName: monitoring-alloy + valueFiles: + - values-dev-gitops.yaml + + # 배포 대상 클러스터와 네임스페이스입니다. + destination: + server: https://kubernetes.default.svc + namespace: monitoring + + # Git 기준으로 자동 동기화합니다. + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s-argocd/applications/dev/monitoring/monitoring-core.yaml b/k8s-argocd/applications/dev/monitoring/monitoring-core.yaml new file mode 100644 index 0000000..20529d6 --- /dev/null +++ b/k8s-argocd/applications/dev/monitoring/monitoring-core.yaml @@ -0,0 +1,52 @@ +# =================================== +# Dev Monitoring Core +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: monitoring-core-dev + namespace: argocd + + # 라벨 추가 + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: monitoring + pinhouse.co.kr/monitoring-component: core + + # 어노테이션 메타데이터 + annotations: + argocd.argoproj.io/sync-wave: "0" + + # Finalizers를 설정하면 Application 삭제 시 관련 리소스도 함께 삭제됨 + finalizers: + - resources-finalizer.argocd.argoproj.io + +spec: + project: default + + # Helm 소스 설정 + source: + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + path: k8s-helm/releases/monitoring-core + helm: + releaseName: monitoring-core + valueFiles: + - values-dev-gitops.yaml + + # 배포 대상 클러스터 + destination: + server: https://kubernetes.default.svc + namespace: monitoring + + # 동기화 정책 + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/k8s-argocd/applications/dev/monitoring/monitoring-loki.yaml b/k8s-argocd/applications/dev/monitoring/monitoring-loki.yaml new file mode 100644 index 0000000..00fbab3 --- /dev/null +++ b/k8s-argocd/applications/dev/monitoring/monitoring-loki.yaml @@ -0,0 +1,45 @@ +# =================================== +# Dev Monitoring Loki +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: monitoring-loki-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: monitoring + pinhouse.co.kr/monitoring-component: loki + annotations: + argocd.argoproj.io/sync-wave: "1" + finalizers: + - resources-finalizer.argocd.argoproj.io + +spec: + project: default + + # Git 저장소에서 관리하는 매니페스트 경로입니다. + source: + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + path: k8s-helm/releases/monitoring-loki + helm: + releaseName: monitoring-loki + valueFiles: + - values-dev-gitops.yaml + + # 배포 대상 클러스터와 네임스페이스입니다. + destination: + server: https://kubernetes.default.svc + namespace: monitoring + + # Git 기준으로 자동 동기화합니다. + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s-argocd/applications/dev/monitoring/monitoring-tempo.yaml b/k8s-argocd/applications/dev/monitoring/monitoring-tempo.yaml new file mode 100644 index 0000000..f604655 --- /dev/null +++ b/k8s-argocd/applications/dev/monitoring/monitoring-tempo.yaml @@ -0,0 +1,45 @@ +# =================================== +# Dev Monitoring Tempo +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: monitoring-tempo-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: monitoring + pinhouse.co.kr/monitoring-component: tempo + annotations: + argocd.argoproj.io/sync-wave: "1" + finalizers: + - resources-finalizer.argocd.argoproj.io + +spec: + project: default + + # Git 저장소에서 관리하는 매니페스트 경로입니다. + source: + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + path: k8s-helm/releases/monitoring-tempo + helm: + releaseName: monitoring-tempo + valueFiles: + - values-dev-gitops.yaml + + # 배포 대상 클러스터와 네임스페이스입니다. + destination: + server: https://kubernetes.default.svc + namespace: monitoring + + # Git 기준으로 자동 동기화합니다. + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s-argocd/applications/dev/platform.yaml b/k8s-argocd/applications/dev/platform.yaml new file mode 100644 index 0000000..ba6bc01 --- /dev/null +++ b/k8s-argocd/applications/dev/platform.yaml @@ -0,0 +1,41 @@ +# =================================== +# Dev Platform Root +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: platform-root-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: platform + finalizers: + - resources-finalizer.argocd.argoproj.io + +# 스펙 +spec: + project: default + + # Git 저장소에서 관리하는 매니페스트 경로입니다. + source: + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + path: k8s-argocd/applications/dev/platform + directory: + recurse: false + + # 배포 대상 클러스터와 네임스페이스입니다. + destination: + server: https://kubernetes.default.svc + namespace: argocd + + # Git 기준으로 자동 동기화합니다. + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s-argocd/applications/dev/platform/argocd-config.yaml b/k8s-argocd/applications/dev/platform/argocd-config.yaml new file mode 100644 index 0000000..51a313f --- /dev/null +++ b/k8s-argocd/applications/dev/platform/argocd-config.yaml @@ -0,0 +1,42 @@ +# =================================== +# Dev Platform Argo CD Config +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: platform-argocd-config-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: platform + annotations: + # Gateway와 인증서가 준비된 뒤 Argo CD HTTPRoute를 연결합니다. + argocd.argoproj.io/sync-wave: "3" + finalizers: + - resources-finalizer.argocd.argoproj.io + +# 스펙 +spec: + project: default + + # Kustomize 오버레이로 Argo CD 부가 설정을 적용합니다. + source: + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + path: k8s-kustomize/platform/argocd/overlays/dev + + # 배포 대상 클러스터 + destination: + server: https://kubernetes.default.svc + namespace: argocd + + # 동기화 정책 + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s-argocd/applications/dev/platform/cert-manager.yaml b/k8s-argocd/applications/dev/platform/cert-manager.yaml new file mode 100644 index 0000000..a1b4568 --- /dev/null +++ b/k8s-argocd/applications/dev/platform/cert-manager.yaml @@ -0,0 +1,50 @@ +# =================================== +# Dev Platform cert-manager +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: platform-cert-manager-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: platform + annotations: + # cert-manager는 platform-chart가 생성하는 Certificate보다 먼저 준비되어야 합니다. + argocd.argoproj.io/sync-wave: "1" + finalizers: + - resources-finalizer.argocd.argoproj.io + +# 스펙 +spec: + project: default + + # 외부 Helm chart와 Git 저장소 values 파일을 함께 사용합니다. + sources: + - repoURL: https://charts.jetstack.io + chart: cert-manager + targetRevision: v1.16.2 + helm: + releaseName: cert-manager + valueFiles: + - $values/k8s-helm/releases/cert-manager/values-dev.yaml + - repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + ref: values + + # 배포 대상 클러스터 + destination: + server: https://kubernetes.default.svc + namespace: cert-manager + + # 동기화 정책 + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/k8s-argocd/applications/dev/platform/external-secret.yaml b/k8s-argocd/applications/dev/platform/external-secret.yaml new file mode 100644 index 0000000..d93be4c --- /dev/null +++ b/k8s-argocd/applications/dev/platform/external-secret.yaml @@ -0,0 +1,51 @@ +# =================================== +# Dev Platform External Secrets Operator +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: platform-external-secret-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: platform + annotations: + # ExternalSecret 리소스를 해석할 컨트롤러를 먼저 준비합니다. + argocd.argoproj.io/sync-wave: "1" + finalizers: + - resources-finalizer.argocd.argoproj.io + +# 스펙 +spec: + project: default + + # 외부 Helm chart와 Git 저장소 values 파일을 함께 사용합니다. + sources: + - repoURL: https://charts.external-secrets.io + chart: external-secrets + targetRevision: 0.20.4 + helm: + releaseName: external-secrets + valueFiles: + - $values/k8s-helm/releases/external-secret/values-dev.yaml + - repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + ref: values + + # 배포 대상 클러스터 + destination: + server: https://kubernetes.default.svc + namespace: external-secrets + + # 동기화 정책 + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true + - Replace=true diff --git a/k8s-argocd/applications/dev/platform/gateway-api.yaml b/k8s-argocd/applications/dev/platform/gateway-api.yaml new file mode 100644 index 0000000..b5c3844 --- /dev/null +++ b/k8s-argocd/applications/dev/platform/gateway-api.yaml @@ -0,0 +1,43 @@ +# =================================== +# Dev Platform Gateway API CRD +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: platform-gateway-api-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: platform + annotations: + # GatewayClass, Gateway, HTTPRoute보다 먼저 CRD를 설치해야 합니다. + argocd.argoproj.io/sync-wave: "0" + finalizers: + - resources-finalizer.argocd.argoproj.io + +# 스펙 +spec: + project: default + + # Gateway API CRD를 GitHub에서 직접 가져옵니다. + source: + repoURL: https://github.com/kubernetes-sigs/gateway-api + targetRevision: v1.2.1 + path: config/crd/standard + + # CRD는 cluster-scoped 리소스라 argocd 네임스페이스로 보냅니다. + destination: + server: https://kubernetes.default.svc + namespace: argocd + + # 동기화 정책 + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/k8s-argocd/applications/dev/platform/gce-pd-csi-driver.yaml b/k8s-argocd/applications/dev/platform/gce-pd-csi-driver.yaml new file mode 100644 index 0000000..8ea8a2d --- /dev/null +++ b/k8s-argocd/applications/dev/platform/gce-pd-csi-driver.yaml @@ -0,0 +1,43 @@ +# =================================== +# Dev Platform GCE PD CSI Driver +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: platform-gce-pd-csi-driver-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: platform + annotations: + # StorageClass보다 먼저 CSI Driver를 설치해야 합니다. + argocd.argoproj.io/sync-wave: "-1" + finalizers: + - resources-finalizer.argocd.argoproj.io + +# 스펙 +spec: + project: default + + # Kustomize 소스 설정 + source: + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + path: k8s-kustomize/platform/gce-pd-csi-driver + + # CSI Driver는 gce-pd-csi-driver 네임스페이스에 배포됩니다. + destination: + server: https://kubernetes.default.svc + namespace: gce-pd-csi-driver + + # 동기화 정책 + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/k8s-argocd/applications/dev/platform/metrics-server.yaml b/k8s-argocd/applications/dev/platform/metrics-server.yaml new file mode 100644 index 0000000..1a59e62 --- /dev/null +++ b/k8s-argocd/applications/dev/platform/metrics-server.yaml @@ -0,0 +1,50 @@ +# =================================== +# Dev Platform Metrics Server +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: platform-metrics-server-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: platform + annotations: + # kube-system에 설치되는 공용 컨트롤러이므로 플랫폼 계층에서 관리합니다. + argocd.argoproj.io/sync-wave: "1" + finalizers: + - resources-finalizer.argocd.argoproj.io + +# 스펙 +spec: + project: default + + # 외부 Helm chart와 Git 저장소 values 파일을 함께 사용합니다. + sources: + - repoURL: https://kubernetes-sigs.github.io/metrics-server/ + chart: metrics-server + targetRevision: 3.13.0 + helm: + releaseName: metrics-server + valueFiles: + - $values/k8s-helm/releases/metrics-server/values-dev.yaml + - repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + ref: values + + # 배포 대상 클러스터 + destination: + server: https://kubernetes.default.svc + namespace: kube-system + + # 동기화 정책 + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/k8s-argocd/applications/dev/platform/monitoring-httproute.yaml b/k8s-argocd/applications/dev/platform/monitoring-httproute.yaml new file mode 100644 index 0000000..0e159e9 --- /dev/null +++ b/k8s-argocd/applications/dev/platform/monitoring-httproute.yaml @@ -0,0 +1,42 @@ +# =================================== +# Dev Platform Monitoring HTTPRoute +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: platform-monitoring-httproute-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: platform + annotations: + # Gateway와 인증서가 준비된 뒤 HTTPRoute를 연결합니다. + argocd.argoproj.io/sync-wave: "3" + finalizers: + - resources-finalizer.argocd.argoproj.io + +# 스펙 +spec: + project: default + + # Grafana HTTPRoute 설정 + source: + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + path: k8s-kustomize/platform/monitoring/overlays/dev + + # 배포 대상 클러스터 + destination: + server: https://kubernetes.default.svc + namespace: monitoring + + # 동기화 정책 + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s-argocd/applications/dev/platform/nginx-gateway-fabric.yaml b/k8s-argocd/applications/dev/platform/nginx-gateway-fabric.yaml new file mode 100644 index 0000000..de12fcd --- /dev/null +++ b/k8s-argocd/applications/dev/platform/nginx-gateway-fabric.yaml @@ -0,0 +1,50 @@ +# =================================== +# Dev Platform NGINX Gateway Fabric +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: platform-nginx-gateway-fabric-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: platform + annotations: + # Gateway API CRD 설치 이후에 게이트웨이 컨트롤러를 배포합니다. + argocd.argoproj.io/sync-wave: "1" + finalizers: + - resources-finalizer.argocd.argoproj.io + +# 스펙 +spec: + project: default + + # 외부 Helm chart와 Git 저장소 values 파일을 함께 사용합니다. + sources: + - repoURL: ghcr.io/nginx/charts + chart: nginx-gateway-fabric + targetRevision: 2.4.2 + helm: + releaseName: ngf + valueFiles: + - $values/k8s-helm/releases/nginx-gateway-fabric/values-dev.yaml + - repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + ref: values + + # 배포 대상 클러스터 + destination: + server: https://kubernetes.default.svc + namespace: nginx-gateway + + # 동기화 정책 + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/k8s-argocd/applications/dev/platform/platform-resources.yaml b/k8s-argocd/applications/dev/platform/platform-resources.yaml new file mode 100644 index 0000000..c57018a --- /dev/null +++ b/k8s-argocd/applications/dev/platform/platform-resources.yaml @@ -0,0 +1,46 @@ +# =================================== +# Dev Platform Resources +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: platform-resources-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: platform + annotations: + # 컨트롤러가 준비된 뒤 Gateway, Certificate, ExternalSecret 같은 실제 리소스를 생성합니다. + argocd.argoproj.io/sync-wave: "2" + finalizers: + - resources-finalizer.argocd.argoproj.io + +# 스펙 +spec: + project: default + + # 로컬 Helm chart를 사용해 플랫폼 공통 리소스를 배포합니다. + source: + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + path: k8s-helm/platform-chart + helm: + releaseName: pinhouse-platform + valueFiles: + - values-dev.yaml + + # 배포 대상 클러스터 + destination: + server: https://kubernetes.default.svc + namespace: platform-system + + # 동기화 정책 + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s-argocd/applications/dev/platform/storageclass.yaml b/k8s-argocd/applications/dev/platform/storageclass.yaml new file mode 100644 index 0000000..bb1cb87 --- /dev/null +++ b/k8s-argocd/applications/dev/platform/storageclass.yaml @@ -0,0 +1,42 @@ +# =================================== +# Dev Platform StorageClass +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: platform-storageclass-dev + namespace: argocd + labels: + pinhouse.co.kr/environment: dev + pinhouse.co.kr/component: platform + annotations: + # PVC를 사용하는 워크로드보다 먼저 StorageClass를 준비합니다. + argocd.argoproj.io/sync-wave: "0" + finalizers: + - resources-finalizer.argocd.argoproj.io + +# 스펙 +spec: + project: default + + # Kustomize 소스 설정 + source: + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + path: k8s-kustomize/platform/storageclass + + # StorageClass는 cluster-scoped 리소스라 argocd 네임스페이스로 보냅니다. + destination: + server: https://kubernetes.default.svc + namespace: argocd + + # 동기화 정책 + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s-argocd/root-apps/root-dev.yaml b/k8s-argocd/root-apps/root-dev.yaml new file mode 100644 index 0000000..32807c6 --- /dev/null +++ b/k8s-argocd/root-apps/root-dev.yaml @@ -0,0 +1,39 @@ +# =================================== +# App of Apps 패턴 +# Dev 환경의 모든 애플리케이션을 관리 +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: root-dev + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io + +# 스펙 +spec: + project: default + + # Git 저장소에서 관리하는 매니페스트 경로입니다. + source: + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + targetRevision: main + path: k8s-argocd/applications/dev + directory: + recurse: false + + # 배포 대상 클러스터와 네임스페이스입니다. + destination: + server: https://kubernetes.default.svc + namespace: argocd + + # Git 기준으로 자동 동기화합니다. + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s-argocd/root-apps/root-prod.yaml b/k8s-argocd/root-apps/root-prod.yaml index 5b9206a..6cbef48 100644 --- a/k8s-argocd/root-apps/root-prod.yaml +++ b/k8s-argocd/root-apps/root-prod.yaml @@ -19,6 +19,7 @@ spec: project: default # GitOps 소 설정 + # Git 저장소에서 관리하는 매니페스트 경로입니다. source: # Git 리포지토리 URL repoURL: https://github.com/PinHouse/PinHouse_CLOUD @@ -32,11 +33,13 @@ spec: recurse: false # 배포 대상 클러스터 + # 배포 대상 클러스터와 네임스페이스입니다. destination: server: https://kubernetes.default.svc namespace: argocd # 동기화 정책 + # Git 기준으로 자동 동기화합니다. syncPolicy: # GitOps 자동 동기화 설정 automated: diff --git a/k8s-helm/.gitignore b/k8s-helm/.gitignore index 799419b..95a7ef5 100644 --- a/k8s-helm/.gitignore +++ b/k8s-helm/.gitignore @@ -4,6 +4,7 @@ platform-chart/**/*.yaml platform-chart/**/README.md !platform-chart/**/values.yaml +!platform-chart/**/values-dev.yaml !platform-chart/**/values-nonprod.yaml !platform-chart/**/values-prod.yaml !platform-chart/templates/**/*.yaml @@ -19,6 +20,7 @@ releases/**/secrets/*.yaml # ======================================== releases/argocd/values-*.yaml !releases/argocd/*.yaml.example +!releases/argocd/values-dev.yaml # ======================================== # Monitoring 로컬 오버라이드 @@ -26,6 +28,8 @@ releases/argocd/values-*.yaml releases/monitoring-*/values-*.yaml releases/monitoring-*/README.md !releases/monitoring-*/values-prod-gitops.yaml +!releases/monitoring-*/values-dev-gitops.yaml +!releases/monitoring-*/values-nonprod-gitops.yaml # ======================================== # 로컬 오버라이드 파일 diff --git a/k8s-helm/platform-chart/values-dev.yaml b/k8s-helm/platform-chart/values-dev.yaml new file mode 100644 index 0000000..1d387df --- /dev/null +++ b/k8s-helm/platform-chart/values-dev.yaml @@ -0,0 +1,205 @@ +# Dev values + +# SSL 매니저 +certManager: + enabled: true + + # 실제 이메일 주소로 변경 + acmeEmail: "pinhousekr@gmail.com" + + # 비운영 환경에서는 Let's Encrypt Staging issuer로 검증합니다. + letsencrypt: + staging: + enabled: true + production: + enabled: false + + # DNS-01 Challenge (CloudDNS) + cloudDNS: + projectId: "dev-pinhouse" + +# 게이트웨이 +gateway: + enabled: true + name: pinhouse-gateway + namespace: nginx-gateway + gatewayClassName: nginx + + http: + enabled: true + port: 80 + + # HTTP to HTTPS 리다이렉트 (301 Permanent Redirect) + http_redirect: + enabled: true + + # 환경별 리스너 + environments: + - name: dev + enabled: true + tlsSecretName: pinhouse-tls-dev + services: + - name: web + hostname: dev.pinhouse.co.kr + - name: api + hostname: api.dev.pinhouse.co.kr + + - name: stg + enabled: false + tlsSecretName: pinhouse-tls-stg + services: + - name: web + hostname: stg.pinhouse.co.kr + - name: api + hostname: api.stg.pinhouse.co.kr + + # 추가 리스너 + listeners: + argo: + enabled: true + domain: argo.dev.pinhouse.co.kr + tlsSecretName: pinhouse-tls-argo + + grafana: + enabled: true + domain: grafana.dev.pinhouse.co.kr + tlsSecretName: pinhouse-tls-grafana + +# 접근 제어 (Access Control) - 비운영도 운영과 같은 기준으로 차단 +accessControl: + enabled: true + blockedPaths: + - name: api-dev-swagger + hostnames: ["api.dev.pinhouse.co.kr"] + paths: + - "/swagger-ui" + - "/v3/api-docs" + + - name: api-dev-management + hostnames: ["api.dev.pinhouse.co.kr"] + paths: + - "/actuator" + - "/metrics" + - "/prometheus" + + - name: common-dev-metrics + hostnames: ["dev.pinhouse.co.kr"] + paths: + - "/api/metrics" + - "/prometheus" + +# 네트워크 정책 +networkPolicy: + enabled: false + + database: + rds: + cidr: "192.168.0.0/16" + ec2: + ip: "172.16.10.81/32" + + ports: + frontend: 3000 + backend: 8080 + ai: 8000 + postgres: 5432 + redis: 6379 + rabbitmq: 5672 + dns: 53 + http: 80 + https: 443 + + namespaces: + - name: nginx-gateway + enabled: true + - name: app + enabled: true + +# 인증서 +certificates: + enabled: true + issuer: letsencrypt-staging + + certs: + - name: pinhouse-tls-dev + namespace: nginx-gateway + dnsNames: + - dev.pinhouse.co.kr + - api.dev.pinhouse.co.kr + enabled: true + + - name: pinhouse-tls-stg + namespace: nginx-gateway + dnsNames: + - stg.pinhouse.co.kr + - api.stg.pinhouse.co.kr + enabled: false + + - name: pinhouse-tls-argo + namespace: nginx-gateway + dnsNames: + - argo.dev.pinhouse.co.kr + enabled: true + + - name: pinhouse-tls-grafana + namespace: nginx-gateway + dnsNames: + - grafana.dev.pinhouse.co.kr + enabled: true + +# GCP Secret Manager 기반 시크릿 적용 +externalSecrets: + enabled: true + + secretStores: + - kind: ClusterSecretStore + name: gcp-secret-manager + spec: + provider: + gcpsm: + projectID: dev-pinhouse + + secrets: + - name: backend-secret-kv + namespace: dev-app + spec: + refreshInterval: 1h + secretStoreRef: + name: gcp-secret-manager + kind: ClusterSecretStore + target: + name: backend-secret-kv + creationPolicy: Owner + deletionPolicy: Retain + dataFrom: + - find: + name: + regexp: "^Dev_BE_" + conversionStrategy: Default + decodingStrategy: None + rewrite: + - regexp: + source: "^Dev_BE_(.*)$" + target: "$1" + + - name: monitoring-secret-kv + namespace: monitoring + spec: + refreshInterval: 1h + secretStoreRef: + name: gcp-secret-manager + kind: ClusterSecretStore + target: + name: monitoring-secret-kv + creationPolicy: Owner + deletionPolicy: Retain + dataFrom: + - find: + name: + regexp: "^Dev_MONITORING_" + conversionStrategy: Default + decodingStrategy: None + rewrite: + - regexp: + source: "^Dev_MONITORING_(.*)$" + target: "$1" diff --git a/k8s-helm/releases/argocd/values-dev.yaml b/k8s-helm/releases/argocd/values-dev.yaml new file mode 100644 index 0000000..70bdbe4 --- /dev/null +++ b/k8s-helm/releases/argocd/values-dev.yaml @@ -0,0 +1,18 @@ +# Dev 환경용 Argo CD 설정 +global: + domain: argo.dev.pinhouse.co.kr + +configs: + cm: + # Git/Helm 변경 감지 주기. + timeout.reconciliation: "60s" + timeout.reconciliation.jitter: "15s" + + # TLS Termination을 Gateway에서 수행하므로 ArgoCD Server는 insecure 모드로 실행 + params: + server.insecure: "true" + +# ArgoCD Server에 --insecure 플래그 추가 +server: + extraArgs: + - --insecure diff --git a/k8s-helm/releases/calico/values-dev.yaml b/k8s-helm/releases/calico/values-dev.yaml new file mode 100644 index 0000000..f3e2e1f --- /dev/null +++ b/k8s-helm/releases/calico/values-dev.yaml @@ -0,0 +1,8 @@ +installation: + calicoNetwork: + bgp: Disabled + ipPools: + - cidr: 192.168.0.0/16 + encapsulation: VXLAN + natOutgoing: Enabled + nodeSelector: all() diff --git a/k8s-helm/releases/cert-manager/values-dev.yaml b/k8s-helm/releases/cert-manager/values-dev.yaml new file mode 100644 index 0000000..d9d6f2b --- /dev/null +++ b/k8s-helm/releases/cert-manager/values-dev.yaml @@ -0,0 +1,4 @@ +config: + enableGatewayAPI: true +crds: + enabled: true \ No newline at end of file diff --git a/k8s-helm/releases/external-secret/values-dev.yaml b/k8s-helm/releases/external-secret/values-dev.yaml new file mode 100644 index 0000000..5393cb9 --- /dev/null +++ b/k8s-helm/releases/external-secret/values-dev.yaml @@ -0,0 +1,11 @@ +installCRDs: true + +replicaCount: 2 + +resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 200m + memory: 256Mi diff --git a/k8s-helm/releases/metrics-server/values-dev.yaml b/k8s-helm/releases/metrics-server/values-dev.yaml new file mode 100644 index 0000000..cf9876b --- /dev/null +++ b/k8s-helm/releases/metrics-server/values-dev.yaml @@ -0,0 +1,22 @@ +# ======================================== +# Metrics Server 비운영 값 +# ======================================== +replicas: 1 + +# kubeadm 기반 노드에서 kubelet 메트릭을 안정적으로 수집하기 위한 기본 인자입니다. +defaultArgs: + - --cert-dir=/tmp + - --secure-port=10250 + - --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP + - --kubelet-use-node-status-port + - --kubelet-insecure-tls + - --metric-resolution=15s + +# 기본 클러스터 규모에 맞춘 리소스 요청값입니다. +resources: + requests: + cpu: 100m + memory: 200Mi + limits: + cpu: 200m + memory: 300Mi diff --git a/k8s-helm/releases/monitoring-alloy/values-dev-gitops.yaml b/k8s-helm/releases/monitoring-alloy/values-dev-gitops.yaml new file mode 100644 index 0000000..d74fb61 --- /dev/null +++ b/k8s-helm/releases/monitoring-alloy/values-dev-gitops.yaml @@ -0,0 +1,21 @@ +# ======================================== +# Dev Monitoring Alloy GitOps 값 +# ======================================== + +# 애플리케이션 OTLP 엔드포인트 예시 +# gRPC: monitoring-alloy.monitoring.svc.cluster.local:4317 +# HTTP: http://monitoring-alloy.monitoring.svc.cluster.local:4318/v1/traces + +global: + # Alloy가 Loki로 보내는 로그 라벨과 공통 식별값에 사용합니다. + clusterName: "pinhouse-dev" + environment: "dev" + +alloy: + resources: + requests: + cpu: 300m + memory: 512Mi + limits: + cpu: 1000m + memory: 1Gi diff --git a/k8s-helm/releases/monitoring-core/values-dev-gitops.yaml b/k8s-helm/releases/monitoring-core/values-dev-gitops.yaml new file mode 100644 index 0000000..96ea569 --- /dev/null +++ b/k8s-helm/releases/monitoring-core/values-dev-gitops.yaml @@ -0,0 +1,91 @@ +# ======================================== +# Dev Monitoring Core GitOps 값 +# ======================================== + +kube-prometheus-stack: + grafana: + # Grafana 관리자 계정은 ExternalSecret이 만든 Kubernetes Secret을 사용합니다. + admin: + existingSecret: monitoring-secret-kv + userKey: GRAFANA_ADMIN_USER + passwordKey: GRAFANA_ADMIN_PASSWORD + + # Grafana Sidecar를 통한 대시보드 자동 로딩 설정 + sidecar: + dashboards: + enabled: true + # ConfigMap에서 찾을 라벨 + label: grafana_dashboard + labelValue: "1" + # 모든 네임스페이스에서 대시보드 ConfigMap 검색 + searchNamespace: ALL + # 폴더 어노테이션 지원 + folderAnnotation: grafana_folder + # 대시보드 프로비저닝 설정 + provider: + foldersFromFilesStructure: true + + # 비운영도 데이터 보존과 설정 확인을 위해 PVC를 사용합니다. + persistence: + enabled: true + type: sts + size: 20Gi + storageClassName: "gce-standard-rwo" + + prometheus: + prometheusSpec: + # 비운영 환경 기준 메트릭 보관 기간과 최대 디스크 사용량입니다. + retention: 15d + retentionSize: 50GB + # 실제 scrape는 Alloy가 수행하고, Prometheus는 remote write receiver로 메트릭을 받습니다. + enableRemoteWriteReceiver: true + + # Prometheus가 직접 ServiceMonitor/PodMonitor/Probe를 scrape하지 않도록 둡니다. + serviceMonitorSelectorNilUsesHelmValues: false + serviceMonitorSelector: + matchLabels: + "pinhouse.co.kr/scrape-via": "prometheus" + serviceMonitorNamespaceSelector: {} + + podMonitorSelectorNilUsesHelmValues: false + podMonitorSelector: + matchLabels: + "pinhouse.co.kr/scrape-via": "prometheus" + podMonitorNamespaceSelector: {} + + probeSelectorNilUsesHelmValues: false + probeSelector: + matchLabels: + "pinhouse.co.kr/scrape-via": "prometheus" + probeNamespaceSelector: {} + + # 메트릭 보관을 위해 PVC를 사용합니다. + storageSpec: + volumeClaimTemplate: + spec: + storageClassName: "gce-standard-rwo" + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 50Gi + + alertmanager: + # Alertmanager는 알림 라우팅 전용이라 상대적으로 작은 리소스로 시작합니다. + # 비어 있는 map을 유지해 차트 기본값과 타입 충돌이 나지 않도록 합니다. + alertmanagerSpec: {} + + prometheusOperator: + # Operator는 scrape 주체는 아니지만 Prometheus/Alertmanager 리소스를 생성하므로 계속 필요합니다. + # 비어 있는 map을 유지해 차트 기본값을 그대로 사용합니다. + resources: {} + + kube-state-metrics: + # Kubernetes 리소스 상태 수집용 컴포넌트입니다. + # 비어 있는 map을 유지해 차트 기본값을 그대로 사용합니다. + resources: {} + + prometheus-node-exporter: + # 노드 메트릭 수집기라서 가볍게 시작하되 과도한 사용을 막기 위해 제한을 둡니다. + # 비어 있는 map을 유지해 차트 기본값을 그대로 사용합니다. + resources: {} diff --git a/k8s-helm/releases/monitoring-loki/values-dev-gitops.yaml b/k8s-helm/releases/monitoring-loki/values-dev-gitops.yaml new file mode 100644 index 0000000..5828237 --- /dev/null +++ b/k8s-helm/releases/monitoring-loki/values-dev-gitops.yaml @@ -0,0 +1,46 @@ +# ======================================== +# Dev Monitoring Loki GitOps 값 +# ======================================== + +loki: + # Loki 공용 ServiceAccount 이름을 고정해 두면 추후 인증 방식 전환 시 추적이 쉽습니다. + serviceAccount: + create: true + name: monitoring-loki-sa + annotations: {} + + loki: + # 로그 보관 기간은 14일로 유지합니다. + limits_config: + retention_period: 336h + + # Loki 로그 청크와 인덱스는 GCS 버킷에 저장합니다. + storage: + bucketNames: + chunks: "pinhouse-dev-loki" + ruler: "pinhouse-dev-loki" + admin: "pinhouse-dev-loki" + gcs: + bucket_name: "pinhouse-dev-loki" + + singleBinary: + replicas: 1 + + persistence: + enabled: true + size: 30Gi + storageClass: "gce-standard-rwo" + + resources: + requests: + cpu: 500m + memory: 512Mi + limits: + cpu: 2000m + memory: 1Gi + + chunksCache: + enabled: false + + resultsCache: + allocatedMemory: 256 diff --git a/k8s-helm/releases/monitoring-tempo/values-dev-gitops.yaml b/k8s-helm/releases/monitoring-tempo/values-dev-gitops.yaml new file mode 100644 index 0000000..a9fa73a --- /dev/null +++ b/k8s-helm/releases/monitoring-tempo/values-dev-gitops.yaml @@ -0,0 +1,100 @@ +# ======================================== +# Dev Monitoring Tempo GitOps 값 +# ======================================== + +tempo: + # Tempo 공용 ServiceAccount 이름을 고정해 두면 인증 설정을 추적하기 쉽습니다. + serviceAccount: + create: true + name: monitoring-tempo-sa + annotations: {} + + storage: + # 트레이스 원본 데이터는 GCS 버킷에 저장합니다. + trace: + gcs: + bucket_name: "pinhouse-dev-tempo" + block: + retention: 336h + + ingester: + replicas: 1 + persistence: + enabled: true + size: 20Gi + storageClass: "gce-standard-rwo" + resources: + requests: + cpu: 500m + memory: 1Gi + limits: + cpu: 1000m + memory: 2Gi + + distributor: + replicas: 1 + resources: + requests: + cpu: 200m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + + compactor: + replicas: 1 + config: + compaction: + block_retention: 336h + resources: + requests: + cpu: 200m + memory: 512Mi + limits: + cpu: 500m + memory: 1Gi + + querier: + replicas: 1 + resources: + requests: + cpu: 200m + memory: 512Mi + limits: + cpu: 500m + memory: 1Gi + + queryFrontend: + replicas: 1 + resources: + requests: + cpu: 200m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + + overrides: + defaults: + metrics_generator: + processors: + - span-metrics + - local-blocks + + metricsGenerator: + enabled: true + replicas: 1 + resources: + requests: + cpu: 200m + memory: 256Mi + limits: + cpu: 500m + memory: 1Gi + config: + processor: + local_blocks: + flush_to_storage: true + storage: + remote_write: + - url: http://monitoring-core-kube-prome-prometheus.monitoring.svc.cluster.local:9090/api/v1/write diff --git a/k8s-helm/releases/nginx-gateway-fabric/values-dev.yaml b/k8s-helm/releases/nginx-gateway-fabric/values-dev.yaml new file mode 100644 index 0000000..3692f1e --- /dev/null +++ b/k8s-helm/releases/nginx-gateway-fabric/values-dev.yaml @@ -0,0 +1,16 @@ +fullnameOverride: ngf-nginx-gateway-fabric + +# GatewayClass 설정 +nginxGateway: + gatewayClassName: nginx + gatewayControllerName: gateway.nginx.org/nginx-gateway-controller + +# Service 설정 +nginx: + service: + type: NodePort + nodePorts: + - port: 30080 + listenerPort: 80 + - port: 30443 + listenerPort: 443 diff --git a/k8s-kustomize/overlays/dev/backend/deployment.yaml b/k8s-kustomize/overlays/dev/backend/deployment.yaml new file mode 100644 index 0000000..d75dad0 --- /dev/null +++ b/k8s-kustomize/overlays/dev/backend/deployment.yaml @@ -0,0 +1,19 @@ +apiVersion: apps/v1 +kind: Deployment + +# 기본 설정 +metadata: + name: pinhouse-be + +# 스펙 +spec: + replicas: 1 + + template: + metadata: + labels: + environment: dev + + spec: + containers: + - name: pinhouse-be diff --git a/k8s-kustomize/overlays/dev/backend/httproute.yaml b/k8s-kustomize/overlays/dev/backend/httproute.yaml new file mode 100644 index 0000000..8d34bd5 --- /dev/null +++ b/k8s-kustomize/overlays/dev/backend/httproute.yaml @@ -0,0 +1,26 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute + +# 기본 설정 +metadata: + name: backend + +# 스펙 +spec: + parentRefs: + - name: pinhouse-gateway + namespace: nginx-gateway + + # 도메인 명 + hostnames: + - "api.dev.pinhouse.co.kr" + + # 참조 규칙 + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: backend-service-dev + port: 80 diff --git a/k8s-kustomize/overlays/dev/backend/kustomization.yaml b/k8s-kustomize/overlays/dev/backend/kustomization.yaml new file mode 100644 index 0000000..b31c68e --- /dev/null +++ b/k8s-kustomize/overlays/dev/backend/kustomization.yaml @@ -0,0 +1,27 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +# 네임 스페이스 +namespace: dev-app + +resources: + - ../../../base/backend + - httproute.yaml + +nameSuffix: -dev + +# 이미지 수정 +# ArgoCD Image Updater가 자동으로 newTag를 업데이트 +images: + - name: REPLACE_ME + newName: asia-northeast3-docker.pkg.dev/dev-pinhouse/pinhouse-dev-be/pinhouse-server + newTag: latest + +# overlays 수정내용 반영 +patches: + - path: deployment.yaml + target: + group: apps + version: v1 + kind: Deployment + name: pinhouse-be diff --git a/k8s-kustomize/overlays/dev/frontend/deployment.yaml b/k8s-kustomize/overlays/dev/frontend/deployment.yaml new file mode 100644 index 0000000..9aa95b0 --- /dev/null +++ b/k8s-kustomize/overlays/dev/frontend/deployment.yaml @@ -0,0 +1,19 @@ +apiVersion: apps/v1 +kind: Deployment + +# 기본 설정 +metadata: + name: pinhouse-fe + +# 스펙 +spec: + replicas: 2 + + template: + metadata: + labels: + environment: dev + + spec: + containers: + - name: pinhouse-fe diff --git a/k8s-kustomize/overlays/dev/frontend/httproute.yaml b/k8s-kustomize/overlays/dev/frontend/httproute.yaml new file mode 100644 index 0000000..2efbd11 --- /dev/null +++ b/k8s-kustomize/overlays/dev/frontend/httproute.yaml @@ -0,0 +1,26 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute + +# 기본 설정 +metadata: + name: frontend + +# 스펙 +spec: + parentRefs: + - name: pinhouse-gateway + namespace: nginx-gateway + + # 도메인 명 + hostnames: + - "dev.pinhouse.co.kr" + + # 참조 규칙 + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: frontend-service-dev + port: 80 diff --git a/k8s-kustomize/overlays/dev/frontend/kustomization.yaml b/k8s-kustomize/overlays/dev/frontend/kustomization.yaml new file mode 100644 index 0000000..62d6eb8 --- /dev/null +++ b/k8s-kustomize/overlays/dev/frontend/kustomization.yaml @@ -0,0 +1,27 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +# 네임 스페이스 +namespace: dev-app + +resources: + - ../../../base/frontend + - httproute.yaml + +nameSuffix: -dev + +# 이미지 수정 +# ArgoCD Image Updater가 자동으로 newTag를 업데이트 +images: + - name: REPLACE_ME + newName: asia-northeast3-docker.pkg.dev/dev-pinhouse/pinhouse-dev-fe/pinhouse-web + newTag: latest + +# overlays 수정내용 반영 +patches: + - path: deployment.yaml + target: + group: apps + version: v1 + kind: Deployment + name: pinhouse-fe diff --git a/k8s-kustomize/overlays/dev/kustomization.yaml b/k8s-kustomize/overlays/dev/kustomization.yaml new file mode 100644 index 0000000..6b53bca --- /dev/null +++ b/k8s-kustomize/overlays/dev/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +# 반영하는 리소스 +resources: + - frontend + - backend diff --git a/k8s-kustomize/platform/argocd/overlays/dev/httproute.yaml b/k8s-kustomize/platform/argocd/overlays/dev/httproute.yaml new file mode 100644 index 0000000..9e6e879 --- /dev/null +++ b/k8s-kustomize/platform/argocd/overlays/dev/httproute.yaml @@ -0,0 +1,27 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute + +# 기본 설정 +metadata: + name: argocd + namespace: argocd + +# 스펙 +spec: + parentRefs: + - name: pinhouse-gateway + namespace: nginx-gateway + + # 도메인 명 + hostnames: + - "argo.dev.pinhouse.co.kr" + + # 참조 규칙 + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: argocd-server + port: 80 diff --git a/k8s-kustomize/platform/argocd/overlays/dev/kustomization.yaml b/k8s-kustomize/platform/argocd/overlays/dev/kustomization.yaml new file mode 100644 index 0000000..5b89931 --- /dev/null +++ b/k8s-kustomize/platform/argocd/overlays/dev/kustomization.yaml @@ -0,0 +1,9 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - ../../base + - httproute.yaml + +patches: + - path: notifications-cm-context-patch.yaml diff --git a/k8s-kustomize/platform/argocd/overlays/dev/notifications-cm-context-patch.yaml b/k8s-kustomize/platform/argocd/overlays/dev/notifications-cm-context-patch.yaml new file mode 100644 index 0000000..5bc15e4 --- /dev/null +++ b/k8s-kustomize/platform/argocd/overlays/dev/notifications-cm-context-patch.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-notifications-cm + namespace: argocd + +data: + context.argocdUrl: "https://argo.dev.pinhouse.co.kr" diff --git a/k8s-kustomize/platform/monitoring/overlays/dev/httproute.yaml b/k8s-kustomize/platform/monitoring/overlays/dev/httproute.yaml new file mode 100644 index 0000000..bd5fb43 --- /dev/null +++ b/k8s-kustomize/platform/monitoring/overlays/dev/httproute.yaml @@ -0,0 +1,27 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute + +# 기본 설정 +metadata: + name: grafana + namespace: monitoring + +# 스펙 +spec: + parentRefs: + - name: pinhouse-gateway + namespace: nginx-gateway + + # 도메인 명 + hostnames: + - "grafana.dev.pinhouse.co.kr" + + # 참조 규칙 + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: monitoring-core-grafana + port: 80 diff --git a/k8s-kustomize/platform/monitoring/overlays/dev/kustomization.yaml b/k8s-kustomize/platform/monitoring/overlays/dev/kustomization.yaml new file mode 100644 index 0000000..157f4b0 --- /dev/null +++ b/k8s-kustomize/platform/monitoring/overlays/dev/kustomization.yaml @@ -0,0 +1,9 @@ +# ======================================== +# Grafana HTTPRoute (Dev) +# ======================================== + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - httproute.yaml diff --git a/terraform/environments/dev/artifact-registry.tf b/terraform/environments/dev/artifact-registry.tf new file mode 100644 index 0000000..a4e49cc --- /dev/null +++ b/terraform/environments/dev/artifact-registry.tf @@ -0,0 +1,14 @@ +# ======================================== +# Artifact Registry 모듈 +# ======================================== +module "artifact_registry" { + source = "../../modules/artifact-registry" + + project_id = var.project_id + default_location = var.artifact_registry_location + common_tags = var.common_tags + + repositories = var.artifact_registry_repositories + repository_iam_bindings = var.artifact_registry_repository_iam_bindings + repository_iam_members = var.artifact_registry_repository_iam_members +} diff --git a/terraform/environments/dev/backend.tf b/terraform/environments/dev/backend.tf new file mode 100644 index 0000000..9a9d37b --- /dev/null +++ b/terraform/environments/dev/backend.tf @@ -0,0 +1,9 @@ +# ======================================== +# Terraform Backend 설정 +# ======================================== +terraform { + backend "gcs" { + bucket = "pinhouse-dev-state-bucket" + prefix = "terraform/dev/state" + } +} diff --git a/terraform/environments/dev/compute.tf b/terraform/environments/dev/compute.tf new file mode 100644 index 0000000..7965cfe --- /dev/null +++ b/terraform/environments/dev/compute.tf @@ -0,0 +1,100 @@ +# ======================================== +# Kubernetes 마스터 노드 컴퓨트 모듈 +# ======================================== +module "k8s_master_nodes" { + source = "../../modules/compute" + + name_prefix = "${var.project}-${var.environment}-k8s-master" + network = module.vpc.vpc_self_link + subnetwork = module.vpc.subnets["app"].self_link + + # 관리형 인스턴스 그룹 설정 + create_instance_template = true + create_instance_group = true + + instance_group_zone = "${var.region}-a" + instance_group_target_size = var.k8s_master_instance_group_size + + # 마스터 노드는 단일 인스턴스로 고정 운영합니다. + enable_autoscaling = false + + # 공통 인스턴스 설정 + machine_type = var.k8s_master_machine_type + source_image = var.k8s_node_source_image + boot_disk_size_gb = var.k8s_node_boot_disk_size_gb + boot_disk_type = "pd-balanced" + enable_external_ip = false + startup_script = templatefile("${path.module}/scripts/k8s-master-init.sh", { + k8s_pod_cidr = var.k8s_pod_cidr + k8s_service_cidr = var.k8s_service_cidr + calico_version = var.calico_version + }) + tags = ["k8s-master", var.environment] + + # 태그 + common_tags = merge(var.common_tags, { + Service = "Kubernetes" + Role = "Master" + }) + + # 서비스 계정 설정 + service_account_email = var.service_account_email + service_account_scopes = [ + "https://www.googleapis.com/auth/cloud-platform" + ] +} + +# ======================================== +# Kubernetes 워커 노드 컴퓨트 모듈 +# ======================================== +module "k8s_worker_nodes" { + source = "../../modules/compute" + + name_prefix = "${var.project}-${var.environment}-k8s-workers" + network = module.vpc.vpc_self_link + subnetwork = module.vpc.subnets["app"].self_link + + # 관리형 인스턴스 그룹 설정 + create_instance_template = true + create_instance_group = true + + instance_group_zone = "${var.region}-a" + instance_group_target_size = var.k8s_worker_instance_group_size + + # target_size가 0이면 워커 MIG를 완전히 비워둘 수 있도록 오토스케일러를 비활성화합니다. + enable_autoscaling = var.k8s_worker_instance_group_size > 0 ? var.enable_autoscaling : false + autoscaling_min_replicas = var.autoscaling_min_replicas + autoscaling_max_replicas = var.autoscaling_max_replicas + autoscaling_cpu_target = 0.7 + + # 공통 인스턴스 설정 + machine_type = var.k8s_worker_machine_type + source_image = var.k8s_node_source_image + boot_disk_size_gb = var.k8s_node_boot_disk_size_gb + boot_disk_type = "pd-balanced" + enable_external_ip = false + startup_script = file("${path.module}/scripts/k8s-worker-init.sh") + named_ports = [ + { + name = "ngf-http" + port = var.nginx_gateway_http_node_port + }, + { + name = "ngf-https" + port = var.nginx_gateway_https_node_port + } + ] + tags = ["k8s-worker", var.environment] + + # 태그 + common_tags = merge(var.common_tags, { + Service = "Kubernetes" + Role = "Worker" + }) + + # 서비스 계정 설정 + service_account_email = var.service_account_email + service_account_scopes = [ + "https://www.googleapis.com/auth/cloud-platform" + ] +} diff --git a/terraform/environments/dev/firewall.tf b/terraform/environments/dev/firewall.tf new file mode 100644 index 0000000..ad0c134 --- /dev/null +++ b/terraform/environments/dev/firewall.tf @@ -0,0 +1,176 @@ +# ======================================== +# 개발 환경 방화벽 규칙 +# ======================================== +locals { + gcp_load_balancer_health_check_source_ranges = [ + "35.191.0.0/16", + "130.211.0.0/22", + ] + + dev_firewall_rules = merge( + var.create_load_balancer ? { + # 외부 프록시 NLB와 헬스 체크가 워커 NodePort로 접근할 수 있도록 허용합니다. + allow_nginx_gateway_nodeports = { + name = "${var.vpc_name}-allow-nginx-gateway-nodeports" + allow = [ + { + protocol = "tcp" + ports = [ + tostring(var.nginx_gateway_http_node_port), + tostring(var.nginx_gateway_https_node_port), + ] + } + ] + source_ranges = concat( + local.gcp_load_balancer_health_check_source_ranges, + [var.load_balancer_proxy_only_subnet_cidr] + ) + target_tags = ["k8s-worker"] + priority = 1000 + } + } : {}, + { + # 마스터와 워커 노드가 Kubernetes API 서버에 접근할 수 있도록 허용합니다. + allow_k8s_api_from_nodes = { + name = "${var.vpc_name}-allow-k8s-api-from-nodes" + allow = [ + { + protocol = "tcp" + ports = ["6443"] + } + ] + source_tags = ["k8s-master", "k8s-worker"] + target_tags = ["k8s-master"] + priority = 1000 + } + + # 마스터 노드 간 etcd와 컨트롤 플레인 포트를 허용합니다. + allow_k8s_control_plane = { + name = "${var.vpc_name}-allow-k8s-control-plane" + allow = [ + { + protocol = "tcp" + ports = ["2379-2380", "10250", "10257", "10259"] + } + ] + source_tags = ["k8s-master"] + target_tags = ["k8s-master"] + priority = 1000 + } + + # 마스터 노드가 워커 노드 kubelet에 접근할 수 있도록 허용합니다. + allow_kubelet_from_control_plane = { + name = "${var.vpc_name}-allow-kubelet-from-control-plane" + allow = [ + { + protocol = "tcp" + ports = ["10250"] + } + ] + source_tags = ["k8s-master"] + target_tags = ["k8s-worker"] + priority = 1000 + } + + # 노드 간 kube-proxy 헬스 및 프록시 포트를 허용합니다. + allow_kube_proxy_from_nodes = { + name = "${var.vpc_name}-allow-kube-proxy-from-nodes" + allow = [ + { + protocol = "tcp" + ports = ["10256"] + } + ] + source_tags = ["k8s-master", "k8s-worker"] + target_tags = ["k8s-worker"] + priority = 1000 + } + + # Calico BGP 피어링에 필요한 TCP 179 포트를 허용합니다. + allow_calico_bgp = { + name = "${var.vpc_name}-allow-calico-bgp" + allow = [ + { + protocol = "tcp" + ports = ["179"] + } + ] + source_tags = ["k8s-master", "k8s-worker"] + target_tags = ["k8s-master", "k8s-worker"] + priority = 1000 + } + + # Calico VXLAN 터널링 트래픽을 허용합니다. + allow_calico_vxlan = { + name = "${var.vpc_name}-allow-calico-vxlan" + allow = [ + { + protocol = "udp" + ports = ["4789"] + } + ] + source_tags = ["k8s-master", "k8s-worker"] + target_tags = ["k8s-master", "k8s-worker"] + priority = 1000 + } + + # Pod CIDR 대역에서 노드로 들어오는 Calico 워크로드 트래픽을 허용합니다. + allow_calico_pod_cidr = { + name = "${var.vpc_name}-allow-calico-pod-cidr" + allow = [ + { + protocol = "tcp" + ports = ["0-65535"] + }, + { + protocol = "udp" + ports = ["0-65535"] + }, + { + protocol = "icmp" + } + ] + source_ranges = [var.k8s_pod_cidr] + target_tags = ["k8s-master", "k8s-worker"] + priority = 1000 + } + }, + var.enable_iap_ssh ? { + # IAP TCP 터널을 통한 SSH 접근을 허용합니다. + allow_iap_ssh = { + name = "${var.vpc_name}-allow-iap-ssh" + allow = [ + { + protocol = "tcp" + ports = ["22"] + } + ] + source_ranges = var.iap_ssh_source_ranges + target_tags = var.management_target_tags + priority = 1000 + } + } : {}, + { + # Kubernetes 노드 태그를 가진 인스턴스끼리 내부 통신을 허용합니다. + allow_internal = { + name = "${var.vpc_name}-allow-internal" + allow = [ + { + protocol = "tcp" + ports = ["0-65535"] + }, + { + protocol = "udp" + ports = ["0-65535"] + }, + { + protocol = "icmp" + } + ] + source_tags = ["k8s-master", "k8s-worker"] + target_tags = ["k8s-master", "k8s-worker"] + priority = 65534 + } + } + ) +} diff --git a/terraform/environments/dev/iap.tf b/terraform/environments/dev/iap.tf new file mode 100644 index 0000000..3e564ff --- /dev/null +++ b/terraform/environments/dev/iap.tf @@ -0,0 +1,13 @@ +# ======================================== +# IAP SSH 접근 모듈 +# ======================================== +module "iap_access" { + source = "../../modules/iap-access" + + project_id = var.project_id + + enable_iap_ssh = var.enable_iap_ssh + iap_ssh_members = var.iap_ssh_members + iap_ssh_admin_members = var.iap_ssh_admin_members + service_account_email = var.service_account_email +} diff --git a/terraform/environments/dev/load-balancer.tf b/terraform/environments/dev/load-balancer.tf new file mode 100644 index 0000000..1f11677 --- /dev/null +++ b/terraform/environments/dev/load-balancer.tf @@ -0,0 +1,174 @@ +# ======================================== +# 외부 프록시 NLB 기본 로컬 값 +# ======================================== +locals { + load_balancer_name_prefix = "${var.project}-${var.environment}-nlb" +} + +# ======================================== +# 외부 프록시 NLB용 proxy-only 서브넷 +# ======================================== +resource "google_compute_subnetwork" "load_balancer_proxy_only" { + count = var.create_load_balancer ? 1 : 0 + + name = "${local.load_balancer_name_prefix}-proxy-only-subnet" + ip_cidr_range = var.load_balancer_proxy_only_subnet_cidr + region = var.region + network = module.vpc.vpc_self_link + description = "개발 환경 외부 프록시 NLB용 proxy-only subnet" + purpose = "REGIONAL_MANAGED_PROXY" + role = "ACTIVE" +} + +# ======================================== +# 외부 프록시 NLB 공인 IP +# ======================================== +resource "google_compute_address" "load_balancer_ip" { + count = var.create_load_balancer ? 1 : 0 + + name = "${local.load_balancer_name_prefix}-ip" + region = var.region + network_tier = "PREMIUM" +} + +# ======================================== +# HTTP NodePort 헬스 체크 +# ======================================== +resource "google_compute_region_health_check" "nginx_gateway_http" { + count = var.create_load_balancer ? 1 : 0 + + name = "${local.load_balancer_name_prefix}-http-health-check" + region = var.region + check_interval_sec = 5 + timeout_sec = 5 + healthy_threshold = 2 + unhealthy_threshold = 3 + + tcp_health_check { + port = var.nginx_gateway_http_node_port + } +} + +# ======================================== +# HTTPS NodePort 헬스 체크 +# ======================================== +resource "google_compute_region_health_check" "nginx_gateway_https" { + count = var.create_load_balancer ? 1 : 0 + + name = "${local.load_balancer_name_prefix}-https-health-check" + region = var.region + check_interval_sec = 5 + timeout_sec = 5 + healthy_threshold = 2 + unhealthy_threshold = 3 + + tcp_health_check { + port = var.nginx_gateway_https_node_port + } +} + +# ======================================== +# HTTP 백엔드 서비스 +# ======================================== +resource "google_compute_region_backend_service" "nginx_gateway_http" { + count = var.create_load_balancer ? 1 : 0 + + name = "${local.load_balancer_name_prefix}-http-backend-service" + region = var.region + protocol = "TCP" + load_balancing_scheme = "EXTERNAL_MANAGED" + port_name = "ngf-http" + timeout_sec = 30 + session_affinity = "CLIENT_IP" + health_checks = [google_compute_region_health_check.nginx_gateway_http[0].id] + + backend { + group = module.k8s_worker_nodes.instance_group_instance_group + balancing_mode = "UTILIZATION" + max_utilization = 0.6 + capacity_scaler = 1.0 + } +} + +# ======================================== +# HTTPS 백엔드 서비스 +# ======================================== +resource "google_compute_region_backend_service" "nginx_gateway_https" { + count = var.create_load_balancer ? 1 : 0 + + name = "${local.load_balancer_name_prefix}-https-backend-service" + region = var.region + protocol = "TCP" + load_balancing_scheme = "EXTERNAL_MANAGED" + port_name = "ngf-https" + timeout_sec = 30 + session_affinity = "CLIENT_IP" + health_checks = [google_compute_region_health_check.nginx_gateway_https[0].id] + + backend { + group = module.k8s_worker_nodes.instance_group_instance_group + balancing_mode = "UTILIZATION" + max_utilization = 0.8 + capacity_scaler = 1.0 + } +} + +# ======================================== +# HTTP 타깃 TCP 프록시 +# ======================================== +resource "google_compute_region_target_tcp_proxy" "nginx_gateway_http" { + count = var.create_load_balancer ? 1 : 0 + + name = "${local.load_balancer_name_prefix}-http-proxy" + region = var.region + backend_service = google_compute_region_backend_service.nginx_gateway_http[0].id +} + +# ======================================== +# HTTPS 타깃 TCP 프록시 +# ======================================== +resource "google_compute_region_target_tcp_proxy" "nginx_gateway_https" { + count = var.create_load_balancer ? 1 : 0 + + name = "${local.load_balancer_name_prefix}-https-proxy" + region = var.region + backend_service = google_compute_region_backend_service.nginx_gateway_https[0].id +} + +# ======================================== +# HTTP 포워딩 규칙 +# ======================================== +resource "google_compute_forwarding_rule" "nginx_gateway_http" { + count = var.create_load_balancer ? 1 : 0 + + name = "${local.load_balancer_name_prefix}-http-forwarding-rule" + region = var.region + ip_protocol = "TCP" + load_balancing_scheme = "EXTERNAL_MANAGED" + network = module.vpc.vpc_self_link + port_range = "80" + target = google_compute_region_target_tcp_proxy.nginx_gateway_http[0].id + network_tier = "PREMIUM" + ip_address = google_compute_address.load_balancer_ip[0].address + + depends_on = [google_compute_subnetwork.load_balancer_proxy_only] +} + +# ======================================== +# HTTPS 포워딩 규칙 +# ======================================== +resource "google_compute_forwarding_rule" "nginx_gateway_https" { + count = var.create_load_balancer ? 1 : 0 + + name = "${local.load_balancer_name_prefix}-https-forwarding-rule" + region = var.region + ip_protocol = "TCP" + load_balancing_scheme = "EXTERNAL_MANAGED" + network = module.vpc.vpc_self_link + port_range = "443" + target = google_compute_region_target_tcp_proxy.nginx_gateway_https[0].id + network_tier = "PREMIUM" + ip_address = google_compute_address.load_balancer_ip[0].address + + depends_on = [google_compute_subnetwork.load_balancer_proxy_only] +} diff --git a/terraform/environments/dev/outputs.tf b/terraform/environments/dev/outputs.tf new file mode 100644 index 0000000..caae922 --- /dev/null +++ b/terraform/environments/dev/outputs.tf @@ -0,0 +1,139 @@ +# ======================================== +# VPC 출력값 +# ======================================== +output "vpc_id" { + description = "생성된 VPC 네트워크 ID입니다." + value = module.vpc.vpc_id +} + +output "vpc_name" { + description = "생성된 VPC 네트워크 이름입니다." + value = module.vpc.vpc_name +} + +output "vpc_self_link" { + description = "생성된 VPC 네트워크 self link입니다." + value = module.vpc.vpc_self_link +} + +output "subnets" { + description = "생성된 서브넷 정보입니다." + value = module.vpc.subnets +} + +# ======================================== +# 스토리지 출력값 +# ======================================== +output "storage_buckets" { + description = "생성된 스토리지 버킷 정보입니다." + value = module.storage.buckets +} + +output "bucket_urls" { + description = "생성된 버킷 URL 목록입니다." + value = module.storage.bucket_urls +} + +# ======================================== +# Artifact Registry 출력값 +# ======================================== +output "artifact_registry_repositories" { + description = "생성된 Artifact Registry 저장소 정보입니다." + value = module.artifact_registry.repositories +} + +output "artifact_registry_docker_repository_urls" { + description = "생성된 Docker Artifact Registry 저장소 URL 목록입니다." + value = module.artifact_registry.docker_repository_urls +} + +# ======================================== +# Secret Manager 출력값 +# ======================================== +output "secret_manager_secrets" { + description = "생성된 Secret Manager secret 정보입니다." + value = module.secret_manager.secrets +} + +output "secret_manager_secret_ids" { + description = "생성된 Secret Manager secret ID 목록입니다." + value = module.secret_manager.secret_ids +} + +# ======================================== +# Artifact Registry 네트워크 출력값 +# ======================================== +output "artifact_registry_private_access" { + description = "Artifact Registry용 Private Google Access 구성 정보입니다." + value = { + domain_option = module.artifact_registry_private_access.google_api_domain_option + googleapis_private_zone_name = module.artifact_registry_private_access.googleapis_private_zone_name + pkg_dev_private_zone_name = module.artifact_registry_private_access.pkg_dev_private_zone_name + google_api_route_name = module.artifact_registry_private_access.google_api_route_name + direct_connectivity_route_name = module.artifact_registry_private_access.google_api_direct_connectivity_route_name + } +} + +# ======================================== +# Kubernetes 출력값 +# ======================================== +output "k8s_master_instances" { + description = "생성된 Kubernetes 마스터 인스턴스 정보입니다." + value = module.k8s_master_nodes.instances +} + +output "k8s_worker_instances" { + description = "생성된 Kubernetes 워커 인스턴스 정보입니다." + value = module.k8s_worker_nodes.instances +} + +output "k8s_master_instance_group_id" { + description = "생성된 Kubernetes 마스터 인스턴스 그룹 ID입니다." + value = module.k8s_master_nodes.instance_group_id +} + +output "k8s_worker_instance_group_id" { + description = "생성된 Kubernetes 워커 인스턴스 그룹 ID입니다." + value = module.k8s_worker_nodes.instance_group_id +} + +output "instance_group_id" { + description = "생성된 Kubernetes 워커 인스턴스 그룹 ID입니다." + value = module.k8s_worker_nodes.instance_group_id +} + +# ======================================== +# 로드 밸런서 출력값 +# ======================================== +output "load_balancer_ip" { + description = "로드 밸런서 IP 주소입니다." + value = var.create_load_balancer ? google_compute_address.load_balancer_ip[0].address : null +} + +# ======================================== +# Kubernetes 네트워크 출력값 +# ======================================== +output "k8s_network_configuration" { + description = "Kubernetes 및 Calico 네트워크 구성 정보입니다." + value = { + pod_cidr = var.k8s_pod_cidr + service_cidr = var.k8s_service_cidr + calico_version = var.calico_version + encapsulation = "IPIP" + } +} + +# ======================================== +# IAP 접근 출력값 +# ======================================== +output "iap_ssh_configuration" { + description = "IAP SSH 접근 구성 정보입니다." + value = { + enabled = var.enable_iap_ssh + source_ranges = var.enable_iap_ssh ? var.iap_ssh_source_ranges : [] + target_tags = var.management_target_tags + members = module.iap_access.iap_access_members + admin_members = module.iap_access.iap_admin_members + direct_ssh_ranges = var.ssh_source_ranges + } +} diff --git a/terraform/environments/dev/private-google-access.tf b/terraform/environments/dev/private-google-access.tf new file mode 100644 index 0000000..0787d44 --- /dev/null +++ b/terraform/environments/dev/private-google-access.tf @@ -0,0 +1,14 @@ +# ======================================== +# Artifact Registry Private Google Access 모듈 +# ======================================== +module "artifact_registry_private_access" { + source = "../../modules/private-google-access" + + project_id = var.project_id + network_self_link = module.vpc.vpc_self_link + name_prefix = "${var.environment}-artifact-registry" + google_api_domain_option = var.google_api_domain_option + + # Cloud NAT를 사용하므로 route_tags는 비어있습니다. + route_tags = [] +} diff --git a/terraform/environments/dev/provider.tf b/terraform/environments/dev/provider.tf new file mode 100644 index 0000000..b98e2ca --- /dev/null +++ b/terraform/environments/dev/provider.tf @@ -0,0 +1,7 @@ +# ======================================== +# Google Provider 설정 +# ======================================== +provider "google" { + project = var.project_id + region = var.region +} diff --git a/terraform/environments/dev/scripts/k8s-master-init.sh b/terraform/environments/dev/scripts/k8s-master-init.sh new file mode 100755 index 0000000..f0417e7 --- /dev/null +++ b/terraform/environments/dev/scripts/k8s-master-init.sh @@ -0,0 +1,174 @@ +#!/usr/bin/env bash +set -euxo pipefail + +# ======================================== +# 기본 환경 점검 +# ======================================== +if [ "$(id -u)" -ne 0 ]; then + echo "이 스크립트는 root 권한으로 실행해야 합니다." + exit 1 +fi + +export DEBIAN_FRONTEND=noninteractive + +# ======================================== +# Ubuntu 기본 패키지 업데이트 +# ======================================== +apt-get update -y +apt-get upgrade -y +apt-get install -y apt-transport-https ca-certificates curl gpg containerd + +# ======================================== +# swap 비활성화 +# ======================================== +swapoff -a +sed -ri '/\sswap\s/s/^#?/#/' /etc/fstab + +# ======================================== +# Kubernetes 네트워크용 커널 모듈 및 sysctl 설정 +# ======================================== +mkdir -p /etc/modules-load.d /etc/sysctl.d /etc/apt/keyrings /etc/containerd + +cat <<'EOF' >/etc/modules-load.d/k8s.conf +overlay +br_netfilter +EOF + +modprobe overlay +modprobe br_netfilter + +cat <<'EOF' >/etc/sysctl.d/99-kubernetes-cri.conf +net.bridge.bridge-nf-call-iptables = 1 +net.bridge.bridge-nf-call-ip6tables = 1 +net.ipv4.ip_forward = 1 +EOF + +sysctl --system + +# ======================================== +# containerd 설정 +# ======================================== +containerd config default >/etc/containerd/config.toml +sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml +systemctl daemon-reload +systemctl enable --now containerd + +# ======================================== +# Kubernetes apt 저장소 설정 +# ======================================== +curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.35/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg +echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.35/deb/ /' >/etc/apt/sources.list.d/kubernetes.list + +# ======================================== +# Kubernetes 패키지 설치 +# ======================================== +apt-get update -y +apt-get install -y kubelet kubeadm kubectl +apt-mark hold kubelet kubeadm kubectl + +# ======================================== +# kubelet Artifact Registry credential provider 설정 +# ======================================== +mkdir -p /etc/kubernetes /opt/image-credential-provider + +cat <<'PROVIDER_EOF' >/opt/image-credential-provider/gcp-artifact-registry-provider +#!/usr/bin/env bash +set -euo pipefail + +# kubelet 요청 본문은 현재 인증 계산에 사용하지 않으므로 읽고 종료합니다. +cat >/dev/null + +token_response="$(curl -fsSL -H 'Metadata-Flavor: Google' \ + http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token)" +access_token="$(printf '%s' "$${token_response}" | sed -n 's/.*"access_token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')" + +if [ -z "$${access_token}" ]; then + echo "메타데이터 서버에서 Artifact Registry access token을 가져오지 못했습니다." >&2 + exit 1 +fi + +cat </etc/kubernetes/credential-provider-config.yaml +apiVersion: kubelet.config.k8s.io/v1 +kind: CredentialProviderConfig +providers: + - name: gcp-artifact-registry-provider + apiVersion: credentialprovider.kubelet.k8s.io/v1 + matchImages: + - "*.pkg.dev" + defaultCacheDuration: "30m" +EOF + +cat <<'EOF' >/etc/default/kubelet +KUBELET_EXTRA_ARGS="--image-credential-provider-config=/etc/kubernetes/credential-provider-config.yaml --image-credential-provider-bin-dir=/opt/image-credential-provider" +EOF + +# ======================================== +# 서비스 활성화 +# ======================================== +systemctl enable --now kubelet + +# ======================================== +# kubeadm 및 Calico 초기 설정 파일 생성 +# ======================================== +cat </root/kubeadm-config.yaml +apiVersion: kubeadm.k8s.io/v1beta4 +kind: ClusterConfiguration +networking: + podSubnet: ${k8s_pod_cidr} + serviceSubnet: ${k8s_service_cidr} +--- +apiVersion: kubelet.config.k8s.io/v1beta1 +kind: KubeletConfiguration +cgroupDriver: systemd +EOF + +cat </root/calico-custom-resources.yaml +apiVersion: operator.tigera.io/v1 +kind: Installation +metadata: + name: default +spec: + calicoNetwork: + ipPools: + - blockSize: 26 + cidr: ${k8s_pod_cidr} + encapsulation: IPIP + natOutgoing: Enabled + nodeSelector: all() +EOF + +cat </root/install-calico.sh +#!/usr/bin/env bash +set -euxo pipefail + +kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/${calico_version}/manifests/operator-crds.yaml +kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/${calico_version}/manifests/tigera-operator.yaml +kubectl create -f /root/calico-custom-resources.yaml +EOF + +chmod +x /root/install-calico.sh + +# ======================================== +# 후속 작업 안내 +# ======================================== +echo "마스터 노드 초기 설정이 완료되었습니다." +echo "1. kubeadm init --config /root/kubeadm-config.yaml --upload-certs" +echo "2. mkdir -p \$HOME/.kube && cp /etc/kubernetes/admin.conf \$HOME/.kube/config && chown \$(id -u):\$(id -g) \$HOME/.kube/config" +echo "3. /root/install-calico.sh" diff --git a/terraform/environments/dev/scripts/k8s-worker-init.sh b/terraform/environments/dev/scripts/k8s-worker-init.sh new file mode 100755 index 0000000..372d12e --- /dev/null +++ b/terraform/environments/dev/scripts/k8s-worker-init.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +set -euxo pipefail + +# ======================================== +# 기본 환경 점검 +# ======================================== +if [ "$(id -u)" -ne 0 ]; then + echo "이 스크립트는 root 권한으로 실행해야 합니다." + exit 1 +fi + +export DEBIAN_FRONTEND=noninteractive + +# ======================================== +# Ubuntu 기본 패키지 업데이트 +# ======================================== +apt-get update -y +apt-get upgrade -y +apt-get install -y apt-transport-https ca-certificates curl gpg containerd + +# ======================================== +# swap 비활성화 +# ======================================== +swapoff -a +sed -ri '/\sswap\s/s/^#?/#/' /etc/fstab + +# ======================================== +# Kubernetes 네트워크용 커널 모듈 및 sysctl 설정 +# ======================================== +mkdir -p /etc/modules-load.d /etc/sysctl.d /etc/apt/keyrings /etc/containerd + +cat <<'EOF' >/etc/modules-load.d/k8s.conf +overlay +br_netfilter +EOF + +modprobe overlay +modprobe br_netfilter + +cat <<'EOF' >/etc/sysctl.d/99-kubernetes-cri.conf +net.bridge.bridge-nf-call-iptables = 1 +net.bridge.bridge-nf-call-ip6tables = 1 +net.ipv4.ip_forward = 1 +EOF + +sysctl --system + +# ======================================== +# containerd 설정 +# ======================================== +containerd config default >/etc/containerd/config.toml +sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml +systemctl daemon-reload +systemctl enable --now containerd + +# ======================================== +# Kubernetes apt 저장소 설정 +# ======================================== +curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.35/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg +echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.35/deb/ /' >/etc/apt/sources.list.d/kubernetes.list + +# ======================================== +# Kubernetes 패키지 설치 +# ======================================== +apt-get update -y +apt-get install -y kubelet kubeadm +apt-mark hold kubelet kubeadm + +# ======================================== +# kubelet Artifact Registry credential provider 설정 +# ======================================== +mkdir -p /etc/kubernetes /opt/image-credential-provider + +cat <<'PROVIDER_EOF' >/opt/image-credential-provider/gcp-artifact-registry-provider +#!/usr/bin/env bash +set -euo pipefail + +# kubelet 요청 본문은 현재 인증 계산에 사용하지 않으므로 읽고 종료합니다. +cat >/dev/null + +token_response="$(curl -fsSL -H 'Metadata-Flavor: Google' \ + http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token)" +access_token="$(printf '%s' "${token_response}" | sed -n 's/.*"access_token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')" + +if [ -z "${access_token}" ]; then + echo "메타데이터 서버에서 Artifact Registry access token을 가져오지 못했습니다." >&2 + exit 1 +fi + +cat </etc/kubernetes/credential-provider-config.yaml +apiVersion: kubelet.config.k8s.io/v1 +kind: CredentialProviderConfig +providers: + - name: gcp-artifact-registry-provider + apiVersion: credentialprovider.kubelet.k8s.io/v1 + matchImages: + - "*.pkg.dev" + defaultCacheDuration: "30m" +EOF + +cat <<'EOF' >/etc/default/kubelet +KUBELET_EXTRA_ARGS="--image-credential-provider-config=/etc/kubernetes/credential-provider-config.yaml --image-credential-provider-bin-dir=/opt/image-credential-provider" +EOF + +# ======================================== +# 서비스 활성화 +# ======================================== +systemctl enable --now kubelet + +# ======================================== +# 후속 작업 안내 +# ======================================== +echo "워커 노드 초기 설정이 완료되었습니다. 이후 kubeadm join 명령을 실행하세요." diff --git a/terraform/environments/dev/secret-manager.tf b/terraform/environments/dev/secret-manager.tf new file mode 100644 index 0000000..fb2c4c9 --- /dev/null +++ b/terraform/environments/dev/secret-manager.tf @@ -0,0 +1,13 @@ +# ======================================== +# Secret Manager 모듈 +# ======================================== +module "secret_manager" { + source = "../../modules/secret-manager" + + project_id = var.project_id + common_tags = var.common_tags + + secret_ids = var.secret_manager_secret_ids + secret_iam_bindings = var.secret_manager_secret_iam_bindings + secret_iam_members = var.secret_manager_secret_iam_members +} diff --git a/terraform/environments/dev/storage.tf b/terraform/environments/dev/storage.tf new file mode 100644 index 0000000..56890ad --- /dev/null +++ b/terraform/environments/dev/storage.tf @@ -0,0 +1,75 @@ +# ======================================== +# 스토리지 모듈 +# ======================================== +module "storage" { + source = "../../modules/storage" + + project_id = var.project_id + default_location = var.storage_location + + # 태그 + common_tags = merge(var.common_tags, { + Service = "Storage" + }) + + # 버킷 정의 + buckets = merge( + + # 기본 버킷 추가 + var.create_storage_buckets ? tomap({ + static_assets = { + name = "${var.project}-${var.environment}" + storage_class = "STANDARD" + uniform_bucket_level_access = true + versioning_enabled = true + force_destroy = true # 개발 환경에서는 빠른 정리를 위해 강제 삭제를 허용합니다. + public_access_prevention = "enforced" + + # 365일이 지난 정적 파일은 Nearline 클래스로 이동합니다. + lifecycle_rules = [ + { + action = { + type = "SetStorageClass" + storage_class = "NEARLINE" + } + condition = { + age = 365 + } + } + ] + + # 정적 자산 제공을 위한 CORS 설정입니다. + cors = length(var.allowed_cors_origins) > 0 ? [ + { + origin = var.allowed_cors_origins + method = ["GET", "HEAD"] + response_header = ["Content-Type"] + max_age_seconds = 3600 + } + ] : [] + } + }) : tomap({}), + + # 모니터링 버킷 추가 + var.create_monitoring_buckets ? tomap({ + loki = { + name = "${var.project}-${var.environment}-${var.monitoring_loki}" + storage_class = "STANDARD" + uniform_bucket_level_access = true + versioning_enabled = true + force_destroy = true # 개발 환경에서는 빠른 정리를 위해 강제 삭제를 허용합니다. + public_access_prevention = "enforced" + cors = [] + } + tempo = { + name = "${var.project}-${var.environment}-${var.monitoring_tempo}" + storage_class = "STANDARD" + uniform_bucket_level_access = true + versioning_enabled = true + force_destroy = true # 개발 환경에서는 빠른 정리를 위해 강제 삭제를 허용합니다. + public_access_prevention = "enforced" + cors = [] + } + }) : tomap({}), + ) +} diff --git a/terraform/environments/dev/terraform.tfvars.example b/terraform/environments/dev/terraform.tfvars.example new file mode 100644 index 0000000..d0e6689 --- /dev/null +++ b/terraform/environments/dev/terraform.tfvars.example @@ -0,0 +1,102 @@ +# ======================================== +# 프로젝트 기본 값 +# ======================================== +project_id = "your-prod-gcp-project-id" +project = "pinhouse" + +# ======================================== +# 배포 환경 기본 값 +# ======================================== +region = "asia-northeast3" +environment = "dev" + +# ======================================== +# VPC 관련 값 +# ======================================== +vpc_name = "dev-vpc" + +# 직접 SSH를 열지 않고 IAP만 사용할 경우 빈 배열로 둡니다. +ssh_source_ranges = [] + +enable_iap_ssh = true +iap_ssh_members = ["group:platform@example.com"] +iap_ssh_admin_members = ["group:sre-admin@example.com"] + +enable_nat = true + +# ======================================== +# Artifact Registry 관련 값 +# ======================================== +artifact_registry_location = "asia-northeast3" + +artifact_registry_repositories = { + fe = { + repository_id = "pinhouse-dev-fe" + format = "DOCKER" + description = "프로덕션 환경용 프런트엔드 이미지 저장소" + immutable_tags = false + } + be = { + repository_id = "pinhouse-dev-be" + format = "DOCKER" + description = "프로덕션 환경용 백엔드 이미지 저장소" + immutable_tags = false + } +} + +google_api_domain_option = "private.googleapis.com" + +# ======================================== +# Kubernetes 컴퓨트 관련 값 +# ======================================== +k8s_master_instance_group_size = 1 +k8s_worker_instance_group_size = 2 + +enable_autoscaling = true +autoscaling_min_replicas = 2 +autoscaling_max_replicas = 5 + +k8s_master_machine_type = "e2-custom-2-4096" +k8s_worker_machine_type = "e2-custom-2-4096" +k8s_node_boot_disk_size_gb = 50 +k8s_node_source_image = "ubuntu-os-cloud/ubuntu-2204-lts" +k8s_pod_cidr = "192.168.0.0/16" +k8s_service_cidr = "10.96.0.0/12" +calico_version = "v3.31.4" + +# ======================================== +# 스토리지 관련 값 +# ======================================== +create_storage_buckets = true +create_monitoring_buckets = true +storage_location = "ASIA-NORTHEAST3" + +# CORS 허용 Origin +allowed_cors_origins = [ + "https://www.example.com", + "https://api.example.com", +] + +# ======================================== +# 로드 밸런서 관련 값 +# ======================================== +create_load_balancer = true +load_balancer_proxy_only_subnet_cidr = "10.2.10.0/23" +nginx_gateway_http_node_port = 30080 +nginx_gateway_https_node_port = 30443 + +# ======================================== +# Secret Manager 관련 값 +# ======================================== +secret_manager_secret_ids = [ + "Dev_BE_DB_URL" + ] + +ESO 또는 특정 서비스 계정에 접근 권한을 줄 때만 아래 값을 채웁니다. +secret_manager_secret_iam_members = { + eso = { + secret_id = "Dev_BE_DB_URL" + role = "roles/secretmanager.secretAccessor" + member = "serviceAccount:example-secrets@example-pinhouse.iam.gserviceaccount.com" + } +} \ No newline at end of file diff --git a/terraform/environments/dev/variables.tf b/terraform/environments/dev/variables.tf new file mode 100644 index 0000000..e44f027 --- /dev/null +++ b/terraform/environments/dev/variables.tf @@ -0,0 +1,339 @@ +# ======================================== +# 프로젝트 기본 변수 +# ======================================== +variable "project_id" { + description = "배포 대상 GCP 프로젝트 ID입니다." + type = string + default = "dev-pinhouse" +} + +variable "project" { + description = "현재 프로젝트 이름입니다." + type = string + default = "pinhouse" +} + +variable "region" { + description = "인프라를 배포할 GCP 리전입니다." + type = string + default = "asia-northeast3" +} + +variable "environment" { + description = "현재 Terraform 환경 이름입니다." + type = string + default = "dev" +} + +# ======================================== +# VPC 관련 변수 +# ======================================== +variable "vpc_name" { + description = "생성할 VPC 네트워크 이름입니다." + type = string + default = "dev-vpc" +} + +variable "ssh_source_ranges" { + description = "직접 SSH 접근을 허용할 CIDR 목록입니다. 비워두면 IAP만 허용합니다." + type = list(string) + default = [] +} + +variable "enable_iap_ssh" { + description = "IAP TCP 터널 기반 SSH 접근 허용 여부입니다." + type = bool + default = true +} + +variable "iap_ssh_source_ranges" { + description = "IAP TCP 터널이 인스턴스로 접근할 때 허용할 Google 관리 소스 CIDR 목록입니다." + type = list(string) + default = ["35.235.240.0/20"] +} + +variable "management_target_tags" { + description = "SSH 및 IAP 관리 접근을 허용할 인스턴스 태그 목록입니다." + type = list(string) + default = ["k8s-master", "k8s-worker"] +} + +variable "enable_nat" { + description = "Cloud NAT 사용 여부입니다." + type = bool + default = true +} + +# ======================================== +# IAP 관련 변수 +# ======================================== +variable "iap_ssh_members" { + description = "IAP 터널과 일반 OS Login 권한을 부여할 IAM 주체 목록입니다." + type = list(string) + default = [] +} + +variable "iap_ssh_admin_members" { + description = "IAP 터널과 관리자 OS Login 권한을 부여할 IAM 주체 목록입니다." + type = list(string) + default = [] +} + +# ======================================== +# Artifact Registry 관련 변수 +# ======================================== +variable "artifact_registry_location" { + description = "Artifact Registry 저장소 기본 생성 위치입니다." + type = string + default = "asia-northeast3" +} + +variable "artifact_registry_repositories" { + description = "생성할 Artifact Registry 저장소 정의 목록입니다." + type = map(object({ + repository_id = string + format = string + description = optional(string) + location = optional(string) + immutable_tags = optional(bool) + common_tags = optional(map(string)) + })) + default = { + fe = { + repository_id = "pinhouse-dev-fe" + format = "DOCKER" + description = "개발 환경용 프런트엔드 이미지 저장소" + immutable_tags = false + } + be = { + repository_id = "pinhouse-dev-be" + format = "DOCKER" + description = "개발 환경용 백엔드 이미지 저장소" + immutable_tags = false + } + } +} + +variable "artifact_registry_repository_iam_bindings" { + description = "Artifact Registry 저장소 IAM 바인딩 정의 목록입니다." + type = map(object({ + repository_key = string + role = string + members = list(string) + })) + default = {} +} + +variable "artifact_registry_repository_iam_members" { + description = "Artifact Registry 저장소 IAM 멤버 정의 목록입니다." + type = map(object({ + repository_key = string + role = string + member = string + })) + default = {} +} + +# ======================================== +# Secret Manager 관련 변수 +# ======================================== +variable "secret_manager_secret_ids" { + description = "생성할 Secret Manager 비밀 ID 목록입니다." + type = list(string) + default = [] +} + +variable "secret_manager_secret_iam_bindings" { + description = "Secret Manager 비밀 IAM 바인딩 정의 목록입니다." + type = map(object({ + secret_id = string + role = string + members = list(string) + })) + default = {} +} + +variable "secret_manager_secret_iam_members" { + description = "Secret Manager 비밀 IAM 멤버 정의 목록입니다." + type = map(object({ + secret_id = string + role = string + member = string + })) + default = {} +} + +# ======================================== +# Private Google Access 관련 변수 +# ======================================== +variable "google_api_domain_option" { + description = "Artifact Registry와 Google APIs에 사용할 Private Google Access 도메인 옵션입니다." + type = string + default = "private.googleapis.com" + + validation { + condition = contains(["private.googleapis.com", "restricted.googleapis.com"], var.google_api_domain_option) + error_message = "google_api_domain_option은 private.googleapis.com 또는 restricted.googleapis.com 중 하나여야 합니다." + } +} + +# ======================================== +# 스토리지 관련 변수 +# ======================================== +variable "create_storage_buckets" { + description = "스토리지 버킷 생성 여부입니다." + type = bool + default = true +} + +variable "create_monitoring_buckets" { + description = "모니터링 버킷 생성 여부입니다." + type = bool + default = false +} + +variable "monitoring_loki" { + description = "Loki에서 사용할 모니터링 라벨입니다." + type = string + default = "loki" +} + +variable "monitoring_tempo" { + description = "Tempo에서 사용할 모니터링 라벨입니다." + type = string + default = "tempo" +} + +variable "storage_location" { + description = "스토리지 버킷을 생성할 위치입니다." + type = string + default = "ASIA-NORTHEAST3" +} + +variable "allowed_cors_origins" { + description = "정적 자산 버킷에서 허용할 CORS Origin 목록입니다." + type = list(string) + default = [] +} + +# ======================================== +# 컴퓨트 관련 변수 +# ======================================== +variable "k8s_master_instance_group_size" { + description = "Kubernetes 마스터 관리형 인스턴스 그룹의 목표 인스턴스 수입니다." + type = number + default = 1 +} + +variable "k8s_worker_instance_group_size" { + description = "Kubernetes 워커 관리형 인스턴스 그룹의 목표 인스턴스 수입니다." + type = number + default = 1 # 개발 환경에서는 비용 절감을 위해 1개로 설정합니다. +} + +variable "enable_autoscaling" { + description = "오토스케일링 사용 여부입니다." + type = bool + default = false # 개발 환경에서는 비활성화합니다. +} + +variable "autoscaling_min_replicas" { + description = "오토스케일링 최소 인스턴스 수입니다." + type = number + default = 1 +} + +variable "autoscaling_max_replicas" { + description = "오토스케일링 최대 인스턴스 수입니다." + type = number + default = 3 # 개발 환경에서는 3개로 제한합니다. +} + +variable "k8s_master_machine_type" { + description = "Kubernetes 마스터 노드에 사용할 머신 타입입니다." + type = string + default = "e2-custom-2-4096" +} + +variable "k8s_worker_machine_type" { + description = "Kubernetes 워커 노드에 사용할 머신 타입입니다." + type = string + default = "e2-custom-2-4096" +} + +variable "k8s_node_boot_disk_size_gb" { + description = "Kubernetes 노드 부팅 디스크 크기(GB)입니다." + type = number + default = 50 +} + +variable "k8s_node_source_image" { + description = "Kubernetes 노드 부팅 디스크에 사용할 이미지입니다." + type = string + default = "ubuntu-os-cloud/ubuntu-2204-lts" +} + +variable "k8s_pod_cidr" { + description = "Kubernetes Pod CIDR 대역입니다. Calico IPPool과 kubeadm podSubnet에 동일하게 사용합니다." + type = string + default = "192.168.0.0/16" +} + +variable "k8s_service_cidr" { + description = "Kubernetes Service CIDR 대역입니다." + type = string + default = "10.96.0.0/12" +} + +variable "calico_version" { + description = "초기 설치에 사용할 Calico 오픈소스 릴리스 버전입니다." + type = string + default = "v3.31.4" +} + +variable "service_account_email" { + description = "인스턴스에 연결할 서비스 계정 이메일입니다." + type = string + default = null +} + +# ======================================== +# 로드 밸런서 관련 변수 +# ======================================== +variable "create_load_balancer" { + description = "로드 밸런서 생성 여부입니다." + type = bool + default = false +} + +variable "load_balancer_proxy_only_subnet_cidr" { + description = "외부 프록시 NLB용 proxy-only 서브넷 CIDR 대역입니다." + type = string + default = "10.0.10.0/23" +} + +variable "nginx_gateway_http_node_port" { + description = "NGINX Gateway Fabric HTTP NodePort 포트입니다." + type = number + default = 30080 +} + +variable "nginx_gateway_https_node_port" { + description = "NGINX Gateway Fabric HTTPS NodePort 포트입니다." + type = number + default = 30443 +} + +# ======================================== +# 공통 태그 변수 +# ======================================== +variable "common_tags" { + description = "공통 태그입니다." + type = map(string) + default = { + Project = "pinhouse" + Environment = "dev" + Version = "v1" + ManagedBy = "terraform" + } +} diff --git a/terraform/environments/dev/versions.tf b/terraform/environments/dev/versions.tf new file mode 100644 index 0000000..49f0907 --- /dev/null +++ b/terraform/environments/dev/versions.tf @@ -0,0 +1,13 @@ +# ======================================== +# Terraform 공통 설정 +# ======================================== +terraform { + required_version = ">= 1.0" + + required_providers { + google = { + source = "hashicorp/google" + version = "~> 5.0" + } + } +} diff --git a/terraform/environments/dev/vpc.tf b/terraform/environments/dev/vpc.tf new file mode 100644 index 0000000..1f4cd82 --- /dev/null +++ b/terraform/environments/dev/vpc.tf @@ -0,0 +1,45 @@ +# ======================================== +# VPC 모듈 +# ======================================== +module "vpc" { + source = "../../modules/vpc" + + vpc_name = var.vpc_name + description = "개발 환경용 VPC 네트워크" + routing_mode = "REGIONAL" + + # 서브넷 정의 + subnets = { + web = { + name = "${var.vpc_name}-web-subnet" + ip_cidr_range = "10.0.1.0/24" + region = var.region + description = "웹 서버용 서브넷" + private_ip_google_access = true + } + app = { + name = "${var.vpc_name}-app-subnet" + ip_cidr_range = "10.0.2.0/24" + region = var.region + description = "애플리케이션 서버용 서브넷" + private_ip_google_access = true + } + db = { + name = "${var.vpc_name}-db-subnet" + ip_cidr_range = "10.0.3.0/24" + region = var.region + description = "데이터베이스용 서브넷" + private_ip_google_access = true + } + } + + # 방화벽 규칙 정의 + firewall_rules = local.dev_firewall_rules + + # Cloud NAT 설정 + enable_nat = var.enable_nat + nat_region = var.region + router_asn = 64514 + nat_log_enable = true + nat_log_filter = "ERRORS_ONLY" +}