๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ”Ž Kubernetes

Argo Project๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CI/CD ๊ตฌ์ถ•ํ•˜๊ธฐ

by Seongpyo Hong 2020. 12. 18.

์กธ์—… ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉฐ Spring Boot ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•œ CI/CD๋ฅผ ๊ตฌ์ถ•ํ•ด์•ผํ•˜๋Š” ๋‹ˆ์ฆˆ๊ฐ€ ์žˆ์—ˆ๊ณ  ๋‹ค์–‘ํ•œ ๋„๊ตฌ๋“ค์„ ์‚ดํŽด๋ณด๋‹ค๊ฐ€ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์— ์นœํ™”์ ์ธ Argo ํ”„๋กœ์ ํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” Argo Project์ธ Argo Event์™€ Argo Workflow๋ฅผ ์ด์šฉํ•œ CI์™€ Argo CD๋ฅผ ์ด์šฉํ•œ CD ์•„ํ‚คํ…์ณ๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๊ณผ์ •์— ๋Œ€ํ•ด ๋‹ค๋ค„๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.


CI Overview

์ด๋ฒˆ ๊ธ€์—์„œ ๊ตฌ์ถ•ํ•  CI ๊ณผ์ •์„ ๊ฐ„๋žตํ•˜๊ฒŒ ์†Œ๊ฐœํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. Github Webhook๋ฅผ Argo Event๊ฐ€ ๋ฐ›์•„ CI Workflow ํ™œ์„ฑํ™”
  2. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ˆ˜ํ–‰
  3. ๋„์ปค ์ด๋ฏธ์ง€ ๋นŒ๋“œ
  4. GCR์— ์ด๋ฏธ์ง€ ํ‘ธ์‹œ
  5. Deploy Repository์˜ Kustomize ํŒŒ์ผ ์ˆ˜์ •(์ตœ์‹  ํƒœ๊ทธ๋ฅผ ๋ฐ˜์˜ํ•œ ์ด๋ฏธ์ง€)
  6. Deploy Repository์— Pull Request ์ƒ์„ฑ

Argo Event

์ด์ œ Github์˜ Webhook์„ ํ†ตํ•ด Push Event๋ฅผ ๋ฐ›๋Š” Argo Event๋ฅผ ์ƒ์„ฑํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


Argo Event Architecture

๋จผ์ € Argo Event์˜ ์•„ํ‚คํ…์ณ๋Š” ์•„๋ž˜ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ถœ์ฒ˜ : https://argoproj.github.io/argo-events/concepts/architecture/

 

Argo Event์˜ ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

  • Event Source
    ์™ธ๋ถ€๋กœ๋ถ€ํ„ฐ ๋ฐ›์„ ์ด๋ฒคํŠธ์— ๋Œ€ํ•œ ์„ค์ •์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  • Eventbus
    Event Source์™€ Event Sensor๋ฅผ ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ์ „์†ก ๊ณ„์ธต์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.
  • Event Sensor
    event dependency์˜ ์ง‘ํ•ฉ๊ณผ trigger๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ event dependency ์ง‘ํ•ฉ์€ input, trigger๋Š” output์˜ ๊ฐœ๋…์œผ๋กœ ์ƒ๊ฐํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • Trigger
    Event Sensor์— ์˜ํ•ด ์ˆ˜ํ–‰๋  ํ™œ๋™์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

Custom Resource ์„ค์น˜

๋จผ์ € ํ•„์š”ํ•œ Argo Event CR์„ ๋‹ค์šด๋ฐ›์Šต๋‹ˆ๋‹ค.

> kubectl apply -f \
https://raw.githubusercontent.com/argoproj/argo-events/stable/manifests/namespace-install.yaml

Event Bus ๋ฐฐํฌ

๋‹ค์Œ์œผ๋กœ Event Source์™€ Event Sensor๊ฐ„์˜ ๋ฐ์ดํ„ฐ ์ „์†ก์„ ์œ„ํ•œ Event Bus์„ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค.

kubectl apply -n argo-events -f \
https://raw.githubusercontent.com/argoproj/argo-events/stable/examples/eventbus/native.yaml

Github Argo Event ์—ฐ๋™

admin:repo_hook ๊ถŒํ•œ์„ ๊ฐ€์ง„ Github API ํ† ํฐ์„ ์ƒ์„ฑํ•˜๊ณ  ์ธ์ฝ”๋”ฉํ•˜์—ฌ argo-events ๋„ค์ž„์ŠคํŽ˜์ด์Šค์— ์‹œํฌ๋ฆฟ์œผ๋กœ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

Encoding

echo -n <api-token-key> | base64

github-access

apiVersion: v1
kind: Secret
metadata:
  name: github-access
  namespace: argo-events
type: Opaque
data:
  token: <access token>
  username: <github username>
  password: <github password>

Event Source

argo event repo๋ฅผ ์ฐธ๊ณ ํ•œ github event-source๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

- event-source.yaml

apiVersion: argoproj.io/v1alpha1
kind: EventSource
metadata:
  component: github-event-source
  name: github-event-source
  namespace: argo-events
spec:
  type: "github"
  github:
    source_repo:
      # owner of the repo
      owner: seongpyoHong
      # repository name
      repository: argo-ci-cd
      # Github will send events to following port and endpoint
      webhook:
        # endpoint to listen to events on
        endpoint: /push
        # port to run internal HTTP server on
        port: "12000"
        # HTTP request method to allow. In this case, only POST requests are accepted
        method: POST
        # url the event-source will use to register at Github.
        # This url must be reachable from outside the cluster.
        # The name for the service is in `<event-source-name>-eventsource-svc` format.
        # You will need to create an Ingress or Openshift Route for the event-source service so that it can be reached from GitHub.
        url: "http://github-event-source-eventsource-svc.argo-events.svc:12000"
      # type of events to listen to.
      # following listens to everything, hence *
      # You can find more info on https://developer.github.com/v3/activity/events/types/
      events:
        - "push"

      # apiToken refers to K8s secret that stores the github api token
      apiToken:
        # Name of the K8s secret that contains the access token
        name: github-access
        # Key within the K8s secret whose corresponding value (must be base64 encoded) is access token
        key: token
      # type of the connection between event-source and Github.
      # You should set it to false to avoid man-in-the-middle and other attacks.
      insecure: true
      # Determines if notifications are sent when the webhook is triggered
      active: true
      # The media type used to serialize the payloads
      contentType: json

๊ธฐ์กด์— ์กด์žฌํ•˜๋Š” Gateway๋ผ๋Š” CR์„ ํ†ตํ•ด Event Source์™€ Event Sensor๋ฅผ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ์„œ๋น„์Šค๋ฅผ ์ƒ์„ฑํ–ˆ์—ˆ์ง€๋งŒ, ์ตœ์‹  ๋ฆด๋ฆฌ์ฆˆ ๋ฒ„์ „์—์„œ๋Š” Gateway๊ฐ€ event source์— ํฌํ•จ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋”ฐ๋กœ ์ƒ์„ฑํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.

 

- event-source-svc

apiVersion: v1
kind: Service
metadata:
  name: github-event-source-eventsource-svc
  namespace: argo-events
spec:
  selector:
    eventsource-name: github-event-source
  ports:
  - port: 12000
    targetPort: 12000
  type: LoadBalancer  

๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด Github์˜ webhook์ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ์„œ๋น„์Šค๋ฅผ ์ •์˜ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด <event-source-name>-eventsource-svc ๋ผ๋Š” ์ด๋ฆ„์˜ service๋ฅผ ํ†ตํ•ด ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค.


Event Sensor ์ƒ์„ฑ

๋‹ค์Œ์œผ๋กœ Github Event Source๋ฅผ ๋ฐ›์•„์„œ ์„ค์ •๋œ Trigger๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” Event Sensor๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. CI ๊ณผ์ •์„ ์ „๋ถ€ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์ „์— ์•ž์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ Pod์„ ์ƒ์„ฑํ•˜๋Š” trigger๋ฅผ ์„ค์ •ํ•˜์—ฌ ํ…Œ์ŠคํŠธ ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

- Event Sensor (Pod Create)

apiVersion: argoproj.io/v1alpha1 
kind: Sensor 
metadata: 
  name: webhook 
  namespace: argo-events 
spec: 
  template: 
    serviceAccountName: argo-events-sa 
  dependencies: 
    - name: test-dep
      eventSourceName: github-event-source 
      eventName: source_repo 
  triggers: 
    - template: 
        name: webhook-pod-trigger 
        k8s: 
          group: "" 
          version: v1 
          resource: pods 
          operation: create 
          source: 
            resource: 
              apiVersion: v1 
              kind: Pod 
              metadata: 
                generateName: hello-world- 
              spec: 
                serviceAccountName: argo-events-sa 
                containers: 
                  - name: hello-container 
                    args: 
                    - "hello-world" 
                    command: 
                    - cowsay 
                    image: "docker/whalesay:latest" 
          parameters: 
            - src: 
                dependencyName: test-dep 
              dest: spec.containers.0.args.0

์ด์ œ ๋“ฑ๋กํ•œ Repository์—์„œ event๋ฅผ ๋ณด๋‚ด๋ฉด Event Source์™€ Event Sensor๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋กœ๊ทธ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

 

Event Source Log

{"level":"info","ts":1604321990.023797,"logger":"argo-events.eventsource","caller":"webhook/webhook.go:187","msg":"new event received, dispatching it...","eventSourceName":"github-event-source","eventSourceType":"github","eventName":"source_repo","eventSourceName":"github-event-source","eventName":"source_repo"}

Event Sensor Log

{"level":"info","ts":1604321990.0337672,"logger":"argo-events.sensor","caller":"standard-k8s/standar-k8s.go:163","msg":"creating the object...","sensorName":"webhook"} {"level":"info","ts":1604321990.055359,"logger":"argo-events.sensor","caller":"sensors/listener.go:266","msg":"successfully processed the trigger","sensorName":"webhook","triggerName":"webhook-pod-trigger"}

CI๋ฅผ ์œ„ํ•œ Trigger ์„ค์ •

Pod์„ ์ƒ์„ฑํ•˜๋˜ Trigger์—์„œ ์ด์ œ CI ์ž‘์—… (Run Test, Build Docker Image, Push Image to Registry)๋ฅผ ์ˆ˜ํ–‰ํ•˜๋„๋ก ๋ณ€๊ฒฝํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ๋•Œ, Trigger๋Š” Argo Workflow๋ฅผ ํ™œ์šฉํ•˜์—ฌ Unit Test, Docker Image Build, Docker Image Push ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋„๋ก ์„ค์ •ํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

Why Argo Workflow?

CI์— ์‚ฌ์šฉ๋˜๋Š” Argo Workflow๋Š” ์ด๋Ÿฐ ์žฅ์ ๋“ค์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ๊ฐ ๋‹จ๊ณ„์˜ Job์ด ์„œ๋กœ ๋‹ค๋ฅธ ์ปจํ…Œ์ด๋„ˆ์—์„œ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ด๋ฃจ์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์—, ์‹คํ–‰ ํ™˜๊ฒฝ์˜ ๋…๋ฆฝ์„ฑ์ด ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค.
  2. ํ•˜๋‚˜์˜ ์—ญํ• ๋งŒ์„ ๋‹ด๋‹นํ•˜๋Š” Job์„ ๋…๋ฆฝ์ ์œผ๋กœ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๊ณ  ์ด๋กœ ์ธํ•ด ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ, ๋ชจ๋“  ๊ธฐ์ˆ ์— ์žฅ๋‹จ์ ์ด ์žˆ๋“ฏ์ด Argo Workflow์— ๋Œ€ํ•œ ๋‹จ์ ๋„ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

  1. ๊ฐ Job์€ ๋…๋ฆฝ๋œ Pod์˜ ์ƒ์„ฑ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ Pod์˜ ์ƒ์„ฑ๊ณผ ์‚ญ์ œ์— ๋Œ€ํ•œ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  2. Job๊ฐ„์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜๊ธฐ ์œ„ํ•œ ์ถ”๊ฐ€ Artifact๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

https://coffeewhale.com/kubernetes/workflow/argo/2020/02/14/argo-wf/ ๊ธ€์„ ์ฐธ๊ณ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.


Argo Workflow ์„ค์น˜

๋จผ์ € Argo Workflow ์‹คํ–‰์„ ์œ„ํ•ด ํ•„์š”ํ•œ CR๋“ค์„ ๋‹ค์šด๋ฐ›์Šต๋‹ˆ๋‹ค. ์ด๋•Œ namespace๋Š” argo๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

> kubectl create ns argo 
> kubectl apply -n argo -f \
https://raw.githubusercontent.com/argoproj/argo/stable/manifests/namespace-install.yaml

 

์ดํ›„ CI/CD ๊ณผ์ •์—์„œ git repository์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ์ •๋ณด๋“ค์„ secret์œผ๋กœ ์ •์˜ํ•ด์ค๋‹ˆ๋‹ค.

  1. source / deploy repository์— ๋Œ€ํ•ด ssh private key๊ฐ€ ํ•„์š”ํ•˜๋ฏ€๋กœ 2๊ฐœ์˜ ์‹œํฌ๋ฆฟ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.- ssh-privatekey(source repo)
    apiVersion: v1
    kind: Secret
    metadata:
      name: source-key
      namespace: argo
    type: Opaque
    data:
      ssh-privatekey: <ssh private key for source repository>
    - ssh-privatekey (deploy key)
  2. apiVersion: v1 kind: Secret metadata: name: deploy-key namespace: argo type: Opaque data: ssh-privatekey: <ssh private key for deploy repository>
  1. repository์— push, commit, pull request๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•œ ์ •๋ณด๋ฅผ ๋‹ด์€ secret์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • github-access
  • apiVersion: v1 kind: Secret metadata: name: github-access namespace: argo-events type: Opaque data: token: <access token> username: <github username> password: <github password>


๋‹ค์Œ์œผ๋กœ ํ…Œ์ŠคํŠธ๊ฐ€ ์™„๋ฃŒ๋œ ์ด๋ฏธ์ง€๋ฅผ GCR์— pushํ•˜๊ณ , artifacts๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ์ €์žฅ์†Œ๋กœ GCS๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๊ถŒํ•œ์ด ๋‹ด๊ฒจ์žˆ๋Š” gcp credential secret์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜์—์„œ google.json์€ GCS์™€ GCR์— ๋Œ€ํ•œ IAM์„ ์„ค์ •ํ•œ ๊ตฌ๊ธ€ ์„œ๋น„์Šค ๊ณ„์ •์˜ ํ‚ค ํŒŒ์ผ์ž…๋‹ˆ๋‹ค. GCP์˜ ์„œ๋น„์Šค ๊ณ„์ •์— ๋Œ€ํ•ด์„œ ์ž์„ธํ•œ ์„ค๋ช…์€ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

> kubectl create secret generic argo-gcp --namespace=argo --from-file=google.json

WorkflowTemplate

workflowTemplate์€ workflow๋ฅผ ๋ฏธ๋ฆฌ ์ •์˜ํ•ด๋†“์€ ๋ฆฌ์†Œ์Šค๋กœ event sensor์—์„œ ๊ฐ€์ ธ๋‹ค ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค.

CI ๊ณผ์ •์—์„œ ํ•„์š”ํ•œ workflowTemplate์„ ์ •์˜ํ•˜๊ณ  event sensor์˜ trigger์—์„œ ๊ฐ ๋‹จ๊ณ„์— ํ•„์š”ํ•œ Argo Workflow๋ฅผ ์ƒ์„ฑํ•˜๋„๋ก ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์˜ํ•  ์ 

1. ClusterRole ์ƒ์„ฑ
namespace-install.yaml์œผ๋กœ ์„ค์น˜ํ•œ argo-event-sa ์„œ๋น„์Šค ๊ณ„์ •์€ argo ๋„ค์ž„์ŠคํŽ˜์ด์Šค์˜ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ๊ถŒํ•œ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— clusterRoleBindind์„ ํ†ตํ•ด ์ ‘๊ทผ ๊ถŒํ•œ์„ ์„ค์ •ํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

- ClusterRole & ClusterRoleBinding

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: argo-cluster-role
rules:
  - apiGroups:
      - argoproj.io
    resources:
      - workflows
      - workflowtemplates
    verbs:
      - create
      - get
      - list
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: argo-trigger
  namespace: argo
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: argo-cluster-role
subjects:
  - kind : ServiceAccount
    name: argo-events-sa
    namespace: argo-events
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: argo-trigger
  namespace: argo-events
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: argo-cluster-role
subjects:
  - kind : ServiceAccount
    name: argo-events-sa
    namespace: argo-events

2. Default Artifact ๋“ฑ๋ก

Job ๊ฐ„์— ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›์„ ๋•Œ, parameter์™€ artifact๋ฅผ ํ†ตํ•ด์„œ ์ฃผ๊ณ  ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ค‘ artifact๋ฅผ ์ด์šฉํ•˜๋ ค๋ฉด mino, s3, gcs์™€ ๊ฐ™์€ object storage๋ฅผ default artifact๋กœ ์„ค์ •ํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•œ GCS ์„ค์ •์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ €๋Š” GCS์— ๋Œ€ํ•œ ๊ถŒํ•œ๊ณผ GCR์˜ ๊ถŒํ•œ์„ ๋ชจ๋‘ ํ•˜๋‚˜์˜ ์„œ๋น„์Šค ๊ณ„์ •์— ์„ค์ •ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— GCR์— ๋Œ€ํ•œ secret์„ ์ƒ์„ฑํ•  ๋•Œ์™€ ๊ฐ™์€ credential์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

$ kubectl edit configmap workflow-controller-configmap -n argo # assumes argo was installed in the argo namespace 
... 
data:  
  artifactRepository: |  
    gcs:  
      bucket: argo-ci-artifacts  
      endpoint: storage.googleapis.com  
      serviceAccountKeySecret:  
      name: argo-gcp  
      key: google.json

์ฝ”๋“œ

์ „์ฒด ์ฝ”๋“œ๋Š” ๋„ˆ๋ฌด ๊ธธ์–ด ๋งํฌ๋ฅผ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.

๊ฐ WorkflowTemplate์— ๋Œ€ํ•œ ๊ธฐ๋Šฅ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • git-checkout-private
    source repository๋ฅผ artifact์— checkout ํ•˜๊ณ , commit id๋ฅผ ํ†ตํ•ด docker image์— ์‚ฌ์šฉํ•  tag๋ฅผ ์ƒ์„ฑ
  • run-test
    ์ด ๊ธ€์—์„œ๋Š” Spring Boot ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— gradle์„ ํ†ตํ•ด ์ž‘์„ฑ๋œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  ๋ชจ๋‘ ์„ฑ๊ณต ์‹œ jar ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด artifact์— ์ €์žฅ
  • build-and-push
    ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๊ฐ€ ์„ฑ๊ณตํ•œ source repository์˜ dockerfile์„ ํ†ตํ•ด ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•˜๊ณ  GCR์— ํ‘ธ์‹œ
  • git-new-branch
    deploy repository์— ์ƒˆ๋กœ์šด branch ์ƒ์„ฑ
  • kustomize-image
    deploy repository์— ์žˆ๋Š” kustomization.yaml ํŒŒ์ผ์„ ์œ„์˜ ๊ณผ์ •์—์„œ ํ‘ธ์‹œ๋œ ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํƒœ๊ทธ ์ˆ˜์ •
  • git-commit
    deploy repository์— ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ commit
  • git-pr
    deploy repository์— Pull Request๋ฅผ ์ƒ์„ฑ

Argo Server (UI)

CI๋ฅผ ์ˆ˜ํ–‰ํ•œ ๊ฒฐ๊ณผ๋ฅผ UI๋กœ ๋ณด๊ณ ์‹ถ๋‹ค๋ฉด argo namespace์˜ argo-server๋ฅผ ์™ธ๋ถ€๋กœ ๋…ธ์ถœ์‹œ์ผœ ์ ‘์†ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์œ„์˜ ์„ค์ •์„ ํ†ตํ•œ ๊ฒฐ๊ณผ ํ™”๋ฉด์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.


Argo CD

Argo CD๋Š” GitOps ๋ฐฉ์‹์„ ์ง€์›ํ•˜๋Š” CD ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

GitOps๋ž€?

GitOps๋ž€ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ๋ฉ”๋‹ˆํŽ˜์ŠคํŠธ ์ €์žฅ์†Œ๋กœ github์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์ถœ์ฒ˜ : https://www.weave.works/technologies/gitops/


SSOT

GitOps์˜ ์žฅ์ ์„ ๋ณด๊ธฐ ์ „์— SSOT(Single Source Of Truth), ๋‹จ์ผ ์ง„์‹ค์˜ ์›์ฒœ์— ๋Œ€ํ•ด ๋จผ์ € ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋‹จ์ผ ์ง„์‹ค์˜ ์›์ฒœ์ด๋ž€ ์–ด๋–ค ์ง„์‹ค์— ๋Œ€ํ•œ ์›์ฒœ์ด ๋‹จ ํ•˜๋‚˜๋งŒ ์กด์žฌํ•œ๋‹ค๋Š” ์˜๋ฏธ๋กœ GitOps๋Š” ๋ชจ๋“  ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ ์ •์˜๋ฅผ Git์—์„œ ๊ด€๋ฆฌํ•˜์—ฌ SSOT๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

SSOT๋ฅผ ํ†ตํ•ด ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
1. ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ ์ •์˜๊ฐ€ Git์— ๋ชจ๋‘ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ˜„์žฌ ์ƒํƒœ๋ฅผ ํŒŒ์•…ํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.
2. ๋ฐฐํฌ ๋ฐฉ๋ฒ•์„ ํ†ต์ผํ•˜์˜€๊ธฐ ๋•Œ๋ฌธ์— ์ž๋™ํ™”๊ฐ€ ์šฉ์ดํ•˜๋ฉฐ, ์—ฌ๋Ÿฌ ๋ฐฐํฌ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์‹ค์ˆ˜๊ฐ€ ์ค„์–ด๋“ญ๋‹ˆ๋‹ค.

https://coffeewhale.com/kubernetes/workflow/argo/2020/02/14/argo-wf/ ๊ธ€์„ ์ฐธ๊ณ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.


GitOps์˜ ์žฅ์ ?

weavework์˜ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•œ GitOps์˜ ์žฅ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ์ƒ์‚ฐ์„ฑ ํ–ฅ์ƒ
    ํ†ตํ•ฉ ํ”ผ๋“œ๋ฐฑ ์ œ์–ด ๋ฃจํ”„๋ฅผ ํ†ตํ•œ ์ง€์†์ ์ธ ๋ฐฐํฌ ์ž๋™ํ™”๋Š” ํ‰๊ท  ๋ฐฐํฌ ์‹œ๊ฐ„์„ ๋‹จ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ–ฅ์ƒ๋œ ๊ฐœ๋ฐœ์ž ํ™˜๊ฒฝ
    ๊ฐœ๋ฐœ์ž๋“ค์€ Git์„ ์‚ฌ์šฉํ•˜์—ฌ kubernetes์˜ ๋‚ด๋ถ€๋ฅผ ์•Œ ํ•„์š” ์—†์ด kubernetes์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ์™€ ๊ธฐ๋Šฅ์„ ๋ณด๋‹ค ์‹ ์†ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐœ์„ ๋œ ์•ˆ์ •์„ฑ
    Git ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋ฉด Kubernetes ์™ธ๋ถ€์—์„œ ๋ชจ๋“  ํด๋Ÿฌ์Šคํ„ฐ ๋ณ€๊ฒฝ์— ๋Œ€ํ•œ ํŽธ๋ฆฌํ•œ ๊ฐ์‚ฌ ๋กœ๊ทธ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋†’์€ ์‹ ๋ขฐ์„ฑ
    Git์˜ rollback/revert ๋ฐ fork ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ์•ˆ์ •์ ์ด๊ณ  ์žฌํ˜„ ๊ฐ€๋Šฅํ•œ ๋กค๋ฐฑ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ „์ฒด ์‹œ์Šคํ…œ์ดGit์— ๊ธฐ๋ก๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์žฅ์•  ๋ฐœ์ƒ ํ›„ ๋ณต๊ตฌํ•ด์•ผ ํ•˜๋Š” ๋‹จ์ผ ์ถœ์ฒ˜๋„ ์žˆ์œผ๋ฏ€๋กœ ๋ณต๊ตฌ(MTTR)์‹œ๊ฐ„์„ ๋‹จ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ผ๊ด€์„ฑ ๋ฐ ํ‘œ์ค€ํ™”
    CI, CD๋ฅผ ํฌํ•จํ•œ ์šด์˜์—…๋ฌด๊ฐ€ Git์„ ํ†ตํ•ด์„œ ์ผ๊ด€๋˜๊ฒŒ ๊ด€๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

์ด์ œ Argo CD๋ฅผ ์„ค์น˜ํ•˜๊ณ  CI ์ž‘์—… ๊ฒฐ๊ณผ๋ฌผ์ธ ๋ฉ”๋‹ˆํŽ˜์ŠคํŠธ ํŒŒ์ผ์„ ํ†ตํ•ด ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


Install

๋จผ์ € ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด Argo CD๋ฅผ ์œ„ํ•œ CR์„ ์ƒ์„ฑํ•˜๊ณ  UI์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด argocd-server์˜ ์„œ๋น„์Šค ํƒ€์ž…์„ LoadBalancer๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

kubectl create namespace argocd kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'

Setting

Argo CD ์„ค์ •์€ UI๋ฅผ ํ†ตํ•ด์„œ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. UI๊ฐ€ ๊ทธ๋ฆฌ ์–ด๋ ต์ง€ ์•Š์œผ๋‹ˆ ๋ฉ”์ธ ํŽ˜์ด์ง€์˜ New App์„ ํด๋ฆญํ•ด์„œ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•ด๋ณด๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

 

์ด ์ž‘์—…์„ UI๊ฐ€ ์•„๋‹Œ ์ฝ”๋“œ๋กœ ๊ด€๋ฆฌํ•˜๋ ค๋ฉด Argo CD๊ฐ€ ์ œ๊ณตํ•˜๋Š” Application CR์„ ์ •์˜ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. Application CR์˜ ๋ฉ”๋‹ˆํŽ˜์ŠคํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

application.yaml

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: spring-boot
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
  project: default
  source:
    repoURL: <SSH address of deploy repository>
    targetRevision: master
    path: .
  destination:
    server: https://kubernetes.default.svc
    namespace: default

์ด ๋•Œ, Private Repository๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ssh private key๋‚˜ username/password secret์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. argocd docs๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ repository์— ๋Œ€ํ•œ ์„ค์ •์ด ๋‹ด๊ฒจ์žˆ๋Š” ConfigMap์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

repository.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
  labels:
    app.kubernetes.io/name: argocd-cm
    app.kubernetes.io/part-of: argocd
data:
  repository: |
    - url: <deploy repository url>
      sshPrivateKeySecret:
        name: deploy-key
        key: sshPrivateKey

์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•  ์ ์€ repository์˜ http ์ฃผ์†Œ๊ฐ€ ์•„๋‹Œ url์„ ์ž…๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


Result

์œ„์—์„œ ์ž…๋ ฅํ•œ ์ •๋ณด์— ๋”ฐ๋ผ Springboot Deployment์™€ Service๊ฐ€ ๋ฐฐํฌ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

TODO

sprintboot์—์„œ ํ•„์š”ํ•œ GCS ์ ‘๊ทผ ๊ถŒํ•œ์„ GKE Workload Identity๋ฅผ ํ†ตํ•ด service account๋กœ ํ•ด๊ฒฐํ•˜๋ ค ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ application layer์—์„œ ์ธ์ฆ์„ ์‚ฌ์šฉํ•  ๋•Œ์—๋Š” service account๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ํ™˜๊ฒฝ ๋ณ€์ˆ˜์ธ GOOGLE_APPLICATION_CREDENTIALS ์„ ํ†ตํ•ด key.json์„ ์ฐพ๊ฒŒ ๋˜์–ด GCS API์— ๋Œ€ํ•ด 403์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€๊ณ , ์ด์— ๋”ฐ๋ผ secret์„ ๋ฏธ๋ฆฌ ์„ค์ • ํ•ด๋†“๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ด ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ๋Š” CD ๊ณผ์ •์—์„œ Secret์„ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด Best Practice๋ฅผ ์•Œ์•„๋ณด๊ณ  ์ ์šฉํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. 

๋ธ”๋กœ๊ทธ์— ์‚ฌ์šฉ๋œ ๋ชจ๋“  ์ฝ”๋“œ๋Š” Github์— ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.


์ฐธ๊ณ  ์ž๋ฃŒ

 

๋Œ“๊ธ€