1. 내용
1) AKS 환경에서 DevOps 사례를 만들어보기 위해서 진행하였다.
2) 참고 문서 : AKS usecase: Devops Gitlab CI + ArgoCD
2. 환경 및 구성
1) CI & Repogitory : GitLab
2) CD : ArgoCD
3) Infra : Azure
4) Deploy : Azure Kubernetes Service
5) Image Management: Azure Container Registry
6) Application : Java SpringBoot
3. 아키텍처

4. 테스트 내용
1) 개발용 그룹 생성과 repo 생성
- 가상머신을 생성해서 Gilab을 설치하였다.

- Gitlab에 접속하여 개발용 그룹 및 repo를 생성하였다.


2) 샘플 소스 작성
간단한 spring boot 샘플 소스 코드를 작성하고 index.html파일을 만들어 웹 페이지가 보이도록 한다.
src/main/resource/static/index.html에 작성한다.

3)gitlab-runner 생성
-여기서는 runner를 Azure kubernetes Service Pod로 생성하고 Gitlab 프로젝트와 연결해준다.
- runner를 생성하기 위해서는 helm value 값을 설정해서 배포를 진행한다.
- Group >> runners >> New group runner 생성



- runner만 생성한 것이고 아직 runner pod를 생성하지 않았기 때문에 연결은 되지 않은 상태이다.

- gitlab-runner를 k8s에 생성하기 위해서 helm을 사용한다.
- helm 리소스를 설치하기 위해서는 chart와 values.yaml이 필요하다.
- 아래와 같이 helm repo를 추가한다.
helm repo add gitlab https://charts.gitlab.io
- 아래와 같이 values.yaml파일을 작성한다.
USER-SUPPLIED VALUES:
affinity: {}
checkInterval: 30
concurrent: 10
configMaps: {}
gitlabUrl: http://10.0.1.5
hostAliases: []
image:
image: gitlab-org/gitlab-runner
registry: registry.gitlab.com
imagePullPolicy: IfNotPresent
metrics:
enabled: false
port: 9252
portName: metrics
serviceMonitor:
enabled: false
nodeSelector: {}
podAnnotations: {}
podLabels: {}
podSecurityContext:
fsGroup: 65533
runAsUser: 100
priorityClassName: ""
rbac:
clusterWideAccess: false
create: true
podSecurityPolicy:
enabled: false
resourceNames:
- gitlab-runner
rules:
- resources:
- configmaps
- pods
- pods/attach
- secrets
- services
verbs:
- get
- list
- watch
- create
- patch
- update
- delete
- apiGroups:
- ""
resources:
- pods/exec
verbs:
- create
- patch
- delete
resources: {}
runnerRegistrationToken: GR1348941kMGHRvADrz8Re2Y7b7pS
runners:
builds: {}
cache: {}
config: |
[[runners]]
name = "My Runner"
url = "http://10.0.1.5/"
token = "{{ .Values.runnerRegistrationToken }}"
executor = "kubernetes"
[runners.kubernetes]
namespace = "{{ .Release.Namespace }}"
image = "ubuntu:16.04"
helpers: {}
namespace: gitlab-runner
services: {}
secrets: []
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: false
runAsNonRoot: true
service:
enabled: false
type: ClusterIP
sessionServer:
enabled: false
terminationGracePeriodSeconds: 3600
tolerations: []
volumeMounts: []
volumes: []
- token 값은 다음과 같이 복사해 올 수 있다.
Group >> Project repo >> Setting >> CI/CD >> Project runner Registration token

- values.yml 파일이 작성되었으면 helm install 명령어를 통해서 AKS에 배포한다.
기본형) helm install gitlab-runner -f values.yaml gitlab/gitlab-runner
예시) helm install --namespace gitlab-runner gitlab-runner -f values.yaml gitlab/gitlab-runner
- 정상적으로 배포가 되었으면 다음과 같이 gitlab-runner pod를 확인한다.

- Gitlab 웹브라우저 접속하여 정상적으로 등록이 되었는지 확인한다.
경로를 잘 따라가야한다..

4) CI 파일 작성
- CI 파일을 작성하기 전에 Azure Container Registry를 생성하고 해당 정보들을 가져온다.
- CI 파일 작성은 프로젝트의 루트 디렉터리에 생성해야한다.
- 이름은 .gitlab-ci.yml이다. 여기서 yml을 yaml로 하면 나중에 파이프라인을 돌려 빌드할때 gitlab에서 인식을 못할 수도 있으니 주의해야한다.
- CI 파일의 주 내용은 빌드를 수행하는 명령어인 mvn package jib:build이다.
- 참고로 jib는 구글에서 개발한 라이브러리로 spring boot 소스를 컨테이너 이미지로 생성해 저장소까지 push를 해줘서 자동화에 매우 유용하다 .
- image인 openjdk 버전을 pom.xml 정보와 잘 맞춰야한다.
- gitlab branch 중 어느 branch에 배포할 것인지 설정을 해줘야 파이프라인이 정상적으로 수행되어 CI 된다.
stages:
- build
variables:
build-job:
stage: build
# tags:
# - terzmy-runner-01
image: maven:3.8.5-openjdk-17
before_script:
- apt-get update && apt-get install -y curl jq
script:
- ACR_SERVER="cheolaksacr001.azurecr.io";
ACR_USERNAME="cheolaksacr001";
ACR_PASSWORD="Nac1kAMFZJlAolBT3O0h0o3rzHalZkydRZZohxUO5p+ACRCnsUiz";
- mvn package jib:build -Dimage.tag=$CI_COMMIT_SHORT_SHA -Dacr.server=$ACR_SERVER -Dacr.username=$ACR_USERNAME -Dacr.password=$ACR_PASSWORD
only:
- develop
- /^release-.*$/
- main
- master
except:
- tags
# when: manual # default는 always임
allow_failure: false
5) pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ollacare</groupId>
<artifactId>sas-api</artifactId>
<version>0.0.2-SNAPSHOT</version>
<name>sas-api</name>
<description>SAS-API</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
<image.tag>local</image.tag>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<!-- <acr.server>cheolaksacr001.azurecr.io</acr.server>
<acr.username>cheolaksacr001</acr.username>
<acr.password>Nac1kAMFZJlAolBT3O0h0o3rzHalZkydRZZohxUO5p+ACRCnsUiz</acr.password> -->
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Azure Storage Blob -->
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>12.28.0</version>
</dependency>
<!-- Azure Identity -->
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.5.0</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.3.2</version>
<configuration>
<from>
<!-- <image>openjdk:8-jdk-alpine</image> -->
<image>openjdk:17-jdk-slim</image>
</from>
<to>
<image>${acr.server}/${project.artifactId}</image>
<tags>
<tag>${project.version}</tag>
</tags>
<auth>
<username>${acr.username}</username>
<password>${acr.password}</password>
</auth>
</to>
<container>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
<mainClass>com.ollacare.sas_api.SasApiApplication</mainClass>
</container>
</configuration>
</plugin>
</plugins>
</build>
</project>
6) CI 파이프라인 실행
- gitlab으로 소스코드 업데이트를 하면 gitlab 파이프라인이 자동으로 생겨 수행한다.
- Running 상태에서 정상적으로 수행하고 나면 아래와 같은 이미지로 passed 상태가 된다.
git add .
git commit -m "update"
git remote -v
git remote add origin http://10.0.1.5/sas-api-group/sas_blob.git

- 정의한 버전 0.0.2-SNAPSHOT이 CI 파일에 의해서 Azure Container Registry로 업로드 된 것을 볼 수있다.


7) 서비스 동작 확인
- ACR 이미지를 활용해서 정상적으로 서비스가 동작하는지 확인한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: sas
namespace: terzmy
spec:
replicas: 1
selector:
matchLabels:
app: sas
template:
metadata:
labels:
app: sas
spec:
containers:
- name: web
image: cheolaksacr001.azurecr.io/sas-api:0.0.2-SNAPSHOT
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: sas
namespace: terzmy
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
spec:
type: ClusterIP
selector:
app: sas
ports:
- protocol: TCP
port: 80
targetPort: 8080
- 배포를 진행하고 정상적으로 배포가 된다면 pod에 직접 접속해서 curl localhost:8080을 실행해본다.
- 만약 정상적으로 배포가 된거면 index.html 웹페이지가 정상적으로 보일 것이다.

8) CD 환경 구성
- CD 환경 구성은 ArgoCD로 가능하다.
- ArgoCD는 yaml파일의 변화를 읽어서 배포를 자동으로 수행하는 Auto Sync 기능이 있다.
9) Access Token 생성과 백업
- yaml 파일의 변화를 읽어서 CD할거기 때문에 manifest repo에서 Access 토큰을 생성한다.
- Setting > Access Token


- 생성한 토큰 값은 기억해둬야한다. 꼮!!
ArgoCD에서 manifest repo 연결에 필요한 설정 정보에 입력해야하기 때문이다.

10) ArgoCD에서 manifest repo 설정
- ArgoCD에서 Gitlab repo에 있는 manifest repo를 연결하는 과정이다.
- 백업해두었던 gitlab Access Token을 사용한다.
- ArgoCD의 초기 ID는 admin이고 비밀번호를 알기 위해서는 아래 명령어를 입력하면 된다.
kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath='{.data.password}' | base64 --decode
- 연결을 위해서 Setting >> Repository >> connect repo
(token으로 했을때 안된다면,,, 비밀번호를 입력했는데,,되네요,, 왜 토큰은 안되는건가요 ?)


11) ArgoCD Application 생성
- 10번 과정을 통해서 Gitlab에 있는 manifest repo까지 연결되어 Application 자동 배포를 위한 준비가 완료되었다.
- Application 추가를 통해서 ArgoCD를 통한 배포 설정을 완료할 수 있다.
- 여기서 Auto Sync 설정을 하여 manifest repo의 변화를 감지하여 CD가 가능하도록 한다.

- 소스 경로 입력
- 해당 repo의 deploy 디렉터리 밑에 manifest.yaml이 있다.

- Destination은 배포될 타겟 AKS의 URL을 입력한다.
- Namespace는 해당 AKS에 배포될 Namespace이다.

- 정상적으로 App이 만들어지면 아래 이미지와 같이 Status상태가 된다.


- AKS에 정상적으로 배포되었는지 확인한다.

- pod에 직접 접속하여 정상적으로 동작하고 있는지 확인한다.

12) CI/CD 테스트
- 소스코드를 003으로 바꿔서 CI/CD 테스트를 진행하겠다.
- 구현한 소스에 대해 배포가필요할때는 pom.xml의 project 버전 정보까지 변경하여 commit & push하고 manifest에서도 동일하게 project 버전 정보를 일치하여 commit & push를 진행한다.
- source commit & push

- manifest.yaml

- source code와 manifest 둘 다 commit & push


- Gitlab 파이프라인 동작 확인
CI 동작 확인 후 Azure Container Registry에 003의 이미지 버전이 올라간지 확인한다.


- Argo CD 파이프라인 확인



- 배포된 Pod에 직접들어가서 확인해본다.

13) 외부접속 인프라 구성
- 이제 외부에서 접속할 수 있도록 환경을 구성한다.
- 네트워크 환경 : AGW -> kubernetes-internal (LB) -> Ingress Nginx Controller -> Service -> deployment 순으로 구성된다.
- 현재 Azure Kubernetes Service의 network 모델은 Azure CNI Overlay이다.
- Azure CNI Overlay는 AGIC가 지원되지 않아서 Ingress Nginx Controller를 직접 구성해줘야한다.
- helm으로 ingress nginx controller를 설치한다.
ACR_LOGIN_SERVER=cheolaksacr001.azurecr.io
REGISTRY_NAME=cheolaksacr001
SOURCE_REGISTRY=registry.k8s.io
CONTROLLER_IMAGE=ingress-nginx/controller
CONTROLLER_TAG=v1.8.1
PATCH_IMAGE=ingress-nginx/kube-webhook-certgen
PATCH_TAG=v20230407
DEFAULTBACKEND_IMAGE=defaultbackend-amd64
DEFAULTBACKEND_TAG=1.5
helm install ingress-nginx ingress-nginx/ingress-nginx \
--version 4.7.1 \
--namespace ingress-basic \
--create-namespace \
--set controller.replicaCount=2 \
--set controller.nodeSelector."kubernetes\.io/os"=linux \
--set controller.image.registry=$ACR_LOGIN_SERVER \
--set controller.image.image=$CONTROLLER_IMAGE \
--set controller.image.tag=$CONTROLLER_TAG \
--set controller.image.digest="" \
--set controller.admissionWebhooks.patch.nodeSelector."kubernetes\.io/os"=linux \
--set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-internal"=true \
--set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz \
--set controller.service.externalTrafficPolicy=Local \
--set controller.admissionWebhooks.patch.image.registry=$ACR_LOGIN_SERVER \
--set controller.admissionWebhooks.patch.image.image=$PATCH_IMAGE \
--set controller.admissionWebhooks.patch.image.tag=$PATCH_TAG \
--set controller.admissionWebhooks.patch.image.digest="" \
--set defaultBackend.nodeSelector."kubernetes\.io/os"=linux \
--set defaultBackend.image.registry=$ACR_LOGIN_SERVER \
--set defaultBackend.image.image=$DEFAULTBACKEND_IMAGE \
--set defaultBackend.image.tag=$DEFAULTBACKEND_TAG \
--set defaultBackend.image.digest=""

- kubernetes-internal 생성
IP는 ingress-nginx-controller external IP인 10.244.0.6이다.

- ingress 생성
manifest.yaml에 포함되어있긴한데. 아래 코드를 이용해서 ingress를 생성한다.
ingress rule에 따라서 어떤 host인지 상관없이(*) / 경로를 타면 pod port 80을 통해 Container 10.244.0.100:8080 으로 접속하게 된다. :
apiVersion: apps/v1
kind: Deployment
metadata:
name: sas
namespace: terzmy
spec:
replicas: 1
selector:
matchLabels:
app: sas
template:
metadata:
labels:
app: sas
spec:
containers:
- name: web
image: cheolaksacr001.azurecr.io/sas-api:0.0.3-SNAPSHOT
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: sas
namespace: terzmy
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
spec:
type: ClusterIP
selector:
app: sas
ports:
- protocol: TCP
port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sas-ingress
namespace: terzmy
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: sas
port:
number: 80

14) Application Gateway 생성
- service(kubernetes-internal) ingress nginx controller 생성하였고 Pod ingress-nginx-controller-578dfc74bc-df6xb 통해 ingress 규칙에 따라 application pod에 도달하게 된다.
- 이제 AGW를 생성해서 외부에서 Public IP로 접속 가능하도록 환경을 구성한다.
- AGW 백엔드풀 편집


- AGW 리스너 설정

- AGW 리스너 규칙 설정


- AGW 상태 프로브 테스트 실행


- AGW 백엔드 상태 체크
kubernetes-internal IP 인 10.224.0.6과 AGW가 연결이 잘 되었다.

15) 외부 접근 테스트
- AGW의 Public IP인 52.231.109.9로 외부 접속했을경우 Cotainer Application이 실행되는 것을 볼 수 있다.

'Azure' 카테고리의 다른 글
| Azure Application Insights를 이용한 Application Monitoring (java) (0) | 2024.10.29 |
|---|---|
| [Study] Public & Private AKS Cluster (0) | 2024.10.21 |
| Azure Blob Storage & SAS Token & Azure SDK for Java (1) | 2024.10.11 |
| Managed Grafana & Prometheus를 이용한 AKS Monitoring (0) | 2024.10.04 |
| AKS에서 Grafana Loki & Prometheus를 이용해서 모니터링 (0) | 2024.10.01 |