Skip to content

Commit deb79f9

Browse files
윤혁준윤혁준
authored andcommitted
feat(posts): 남은 글 영어 번역 추가
1 parent 1becbbc commit deb79f9

13 files changed

Lines changed: 3622 additions & 0 deletions

‎content/posts/en/ecs-to-eks-migration.md‎

Lines changed: 496 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
---
2+
title: "GitOps Deployment Automation - Building a Slack Approval Pipeline with ArgoCD, n8n, and GitHub Actions"
3+
date: "2026-02-26"
4+
description: "How I built a GitOps-based deployment automation system during the ECS to EKS migration, combining ArgoCD, n8n, and GitHub Actions with different deployment strategies per environment."
5+
tags: ["kubernetes", "argocd", "gitops", "n8n", "devops", "github-actions"]
6+
draft: false
7+
---
8+
9+
## Background
10+
11+
The service used to run on AWS ECS. The deployment flow was simple: GitHub Actions built a Docker image and directly updated the ECS service. That worked for a while, but several problems appeared as the system grew.
12+
13+
- The CI pipeline had too much authority over production.
14+
- Runtime state was scattered across AWS resources and scripts.
15+
- Rollbacks were not represented clearly in Git.
16+
- Dev, stage, and prod needed different deployment policies.
17+
18+
After moving to EKS, I wanted deployments to follow GitOps. The cluster should converge to the state described in Git, and deployment automation should update Git instead of mutating the cluster directly.
19+
20+
## Overall Architecture
21+
22+
```mermaid
23+
flowchart TB
24+
DEV["dev/stage branch merge"] --> CI["GitHub Actions"]
25+
PROD["prod release trigger"] --> CI
26+
27+
CI --> IMG["Build image"]
28+
IMG --> REG["Push image to registry"]
29+
30+
REG --> UPDATER["ArgoCD Image Updater<br/>(dev/stage)"]
31+
UPDATER --> GIT1["Commit image tag to overlay"]
32+
GIT1 --> ARGO["ArgoCD sync"]
33+
34+
CI --> N8N["n8n workflow<br/>(prod)"]
35+
N8N --> SLACK["Slack approval"]
36+
SLACK --> GIT2["Commit prod image tag"]
37+
GIT2 --> ARGO
38+
39+
ARGO --> EKS["EKS cluster"]
40+
```
41+
42+
The important distinction is that no deployment tool directly edits Kubernetes production objects. It edits Git, and ArgoCD applies the desired state.
43+
44+
### dev / stage - Fully Automatic
45+
46+
For dev and stage, the goal is fast feedback.
47+
48+
1. GitHub Actions builds the image.
49+
2. The image is pushed to the registry.
50+
3. ArgoCD Image Updater detects the new image.
51+
4. It updates the kustomize overlay.
52+
5. ArgoCD syncs the application.
53+
54+
There is no manual approval. If something breaks, the environment is not customer-facing and can be fixed quickly.
55+
56+
### prod - Deploy After Slack Approval
57+
58+
Production has a different policy.
59+
60+
1. GitHub Actions builds the image.
61+
2. n8n receives release metadata.
62+
3. n8n sends a Slack message with approve/reject buttons.
63+
4. If approved, n8n creates a Git commit that updates the prod overlay image tag.
64+
5. ArgoCD syncs the new desired state.
65+
66+
This keeps production deployment auditable. The approval happens in Slack, but the actual deployment state is still Git.
67+
68+
## Core Components
69+
70+
### 1. GitHub Actions - CI Pipeline
71+
72+
GitHub Actions is responsible for build and verification.
73+
74+
```yaml
75+
name: build
76+
77+
on:
78+
push:
79+
branches: [main]
80+
81+
jobs:
82+
build:
83+
runs-on: ubuntu-latest
84+
steps:
85+
- uses: actions/checkout@v4
86+
- name: Build image
87+
run: docker build -t app-api:${{ github.sha }} .
88+
- name: Push image
89+
run: docker push app-api:${{ github.sha }}
90+
```
91+
92+
The CI job does not apply Kubernetes manifests. It only produces an immutable image and metadata.
93+
94+
### 2. ArgoCD - Cluster State Management
95+
96+
ArgoCD watches the Git repository and makes the cluster match it.
97+
98+
```yaml
99+
apiVersion: argoproj.io/v1alpha1
100+
kind: Application
101+
metadata:
102+
name: app-api-prod
103+
spec:
104+
source:
105+
repoURL: https://github.com/example/platform-manifests
106+
path: apps/app-api/overlays/prod
107+
destination:
108+
server: https://kubernetes.default.svc
109+
namespace: app-prod
110+
syncPolicy:
111+
automated:
112+
prune: false
113+
selfHeal: true
114+
```
115+
116+
For prod, auto-sync can still be used because approval happens before Git changes. The Git commit itself is the gate.
117+
118+
### 3. n8n - Slack Approval Workflow
119+
120+
n8n acts as the glue between CI and Slack.
121+
122+
```mermaid
123+
flowchart LR
124+
A["GitHub Actions webhook"] --> B["n8n"]
125+
B --> C["Create Slack approval message"]
126+
C --> D{"Approved?"}
127+
D -->|"Yes"| E["Commit image tag to prod overlay"]
128+
D -->|"No"| F["Stop workflow"]
129+
```
130+
131+
n8n was useful because the workflow is mostly integration logic: receive metadata, format Slack messages, wait for interaction, call GitHub API, and notify the result.
132+
133+
### 4. kustomize - Environment Separation
134+
135+
Each environment has its own overlay.
136+
137+
```text
138+
apps/app-api/
139+
base/
140+
overlays/
141+
dev/
142+
stage/
143+
prod/
144+
```
145+
146+
The production approval workflow only changes the image tag in the prod overlay. It does not touch dev or stage.
147+
148+
## Environment Strategy Comparison
149+
150+
| Environment | Deployment trigger | Approval | Rollback |
151+
|-------------|--------------------|----------|----------|
152+
| dev | merge or image build | none | update overlay |
153+
| stage | merge or image build | none | update overlay |
154+
| prod | release workflow | Slack approval | revert Git commit |
155+
156+
The key is that all environments share the same manifest structure, but not the same policy.
157+
158+
## Things That Went Wrong
159+
160+
### Why I Delayed ArgoCD for prod at First
161+
162+
At first, I considered applying ArgoCD only to dev and stage. Production felt risky because auto-sync sounded like "automatic production deployment."
163+
164+
The important realization was that auto-sync and auto-release are different. ArgoCD auto-sync only applies Git state. If Git changes are controlled by Slack approval, production is still gated.
165+
166+
### Why stage Became Fully Automatic
167+
168+
Stage was initially approval-based too. But that slowed down verification. Stage should be close to production, but it should still move quickly enough to catch deployment problems before prod.
169+
170+
So stage was changed to automatic deployment. Production remained approval-based.
171+
172+
### Automatic Rollback Caused by readiness probe
173+
174+
Some deployments rolled back even though the application eventually became healthy. The readiness probe was too aggressive for the startup time.
175+
176+
The fix was to align Kubernetes readiness timing with Spring Boot startup behavior:
177+
178+
- use the proper readiness actuator endpoint.
179+
- set enough initial delay.
180+
- keep failure threshold realistic.
181+
- do not route traffic before DB and external dependencies are ready.
182+
183+
## Results
184+
185+
- Deployment state moved from CI scripts to Git.
186+
- Dev and stage became faster through automatic deployment.
187+
- Production deployment kept manual approval without losing GitOps.
188+
- Rollback became a Git operation.
189+
- Slack approval history and Git history together gave a clearer audit trail.
190+
191+
## Closing
192+
193+
GitOps is not just "use ArgoCD." The important question is where the source of truth lives.
194+
195+
If CI directly changes the cluster, the source of truth is split between scripts, cluster state, and the repository. If CI only builds artifacts and deployment changes happen through Git, the model becomes much easier to reason about.
196+
197+
The final structure was simple: GitHub Actions builds, n8n approves, Git records, and ArgoCD applies.

0 commit comments

Comments
 (0)