목차
- 원격 저장소에 멀티모듈 빌드 방식으로 프로젝트를 잡아놓고, Dockerfile , K8s Manifest 작성 및 버전관리
- Jenkins 빌드 라인
- 이후 통신 과정 요약
이유
커다란 뜻은 아니고, 좋은 기회가 있어, 개별 소스 관리 방식에서 멀티모듈의 부모 & 자식 관계의 프로젝트에 K8s 빌드라인까지 샘플링할 기회가 생겼다.
프로젝트 구조 및, 템플릿
- 젠킨스 빌드 파이프 라인 까지 세세하게 적지는 않음.
- YAML 템플릿 , setting & build.gradle 파일 정도 정리
일단 프로젝트 구조는 이러하다, 멀티모듈 방식이며, 부모 모듈에서 하위로 중복 디펜던시를 뿌리고, 관리하도록 했다.
간단한 샘플이므로, 해당 프로젝트의 build.gradle 은 모두 비어있는 파일이다.
부모 모듈 setting.gradle
rootProject.name = 'test'
include ':admin-service'
include ':kpi-service'
부모 모듈 build.gradle , 부트 어플리케이션으므로 bootJar 사이클 기입
buildscript {
ext {
springBootVersion = '2.4.5'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath "io.spring.gradle:dependency-management-plugin:1.0.11.RELEASE"
}
}
subprojects {
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.test.sample'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
//implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.4'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
}
project(":admin-service") {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'
}
bootJar{
enabled(true)
}
}
project(":kpi-service"){
ext {
set('springCloudVersion', "2020.0.2")
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j'
}
bootJar{
enabled(true)
}
}
Admin-Service 의 application.yaml
spring:
application:
name: admin-service
datasource:
url: jdbc:postgresql://localhost:5432/postgres?currentSchema=public
username: postgres
password: sample
jpa:
properties:
hibernate:
show_sql: true
format_sql: true
generate-ddl: true
hibernate:
ddl-auto: create-drop
open-in-view: true
logging:
level:
org.hibernate.type.descriptor.sql: trace
server:
port: 8080
management:
endpoints:
web:
exposure:
include:
- info
- health
endpoint:
health:
show-details: always
---
spring:
config:
activate:
on-profile: k8s
datasource:
url: jdbc:postgresql://aws/database?currentSchema=schema
username: test
password: sample
management:
endpoint:
health:
show-details: always
Dockerfile , CPU & Memory 준수 이슈로 12.0.2 사용 [ 추후 Kubenetes 에서 Pod.spec.containers[].resources.requests & limits 관리에 문제가 생기면 안 됨], 도커 이미지 실행 문에 변수 넣어 프로파일 변경
FROM openjdk:12.0.2
EXPOSE 8080
ADD ./build/libs/*.jar app.jar
ENTRYPOINT ["java","-jar","-Dspring.profiles.active=k8s","/app.jar"]
Kubernetes 자원 템플릿, Actuator 의 Detail 설정을 잡아놔서 health 를 호출하면 DB 핑도 같이 찍힌다.
apiVersion: v1
kind: Service
metadata:
name: admin
namespace: sample
spec:
type: ClusterIP
selector:
app: admin-service
proj: sample
type: dev
ports:
- port: 8080
targetPort: 8080
protocol: TCP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: admin
namespace: sample
annotations:
type: dev
function: admin-service
kubernetes.io/change-cause: jenkins build
labels:
app: admin-service
proj: sample
type: dev
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
replicas: 2
selector:
matchLabels:
app: admin-service
proj: sample
type: dev
template:
metadata:
name: admin
namespace: sample
labels:
app: admin-service
proj: sample
type: dev
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: proj
operator: In
values:
- "sample"
- key: app
operator: In
values:
- "admin-service"
topologyKey: "kubernetes.io/hostname"
restartPolicy: Always
terminationGracePeriodSeconds: 10
imagePullSecrets:
- name: sample-docker-regsitry
containers:
- name: admin
image: privateRepo/sample-admin-service:latest
imagePullPolicy: Always
livenessProbe:
httpGet:
port: 8080
path: /api/v1/liveness
failureThreshold: 4
initialDelaySeconds: 7
periodSeconds: 5
timeoutSeconds: 15
readinessProbe:
httpGet:
port: 8080
path: /actuator/health
failureThreshold: 4
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 15
ports:
- containerPort: 8080
젠킨스 모듈 빌딩
- 깃 체크아웃
- 원하는 그레들 사이클
- Docker 작업
- K8s 자원 생성
- 결과 확인
sudo chmod +x ./gradlew
gradlew clean -p ./admin-service
gradlew build -p ./admin-service
...
추후 통신과정
- Ingress 자원은 클라우드 서비스든 온프레미스 환경의 Cluster 든 도메인을 연결했을것이다.
- Ingress.rules[].http & host
- paths[].path.backend.service.name&port ..
- Ingress.rules[].http & host
공식문서 템플릿
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test
port:
number: 80
- 인그레스 컨트롤러가 클라우스 서비스에서 사용하는 LB 를 사용해서 개별 LB 에서 Nginx 를 타고 들어오거나 Ingress Controller 자체가 Nginx 거나 (온프레미스..) 이므로 통해서 진입한다.
별도의 Namespace 를 Cluster 에서 사용 중이니, Servicename.namespacename.svc.cluster.local 로 접근하거나, ExternalName 으로 거쳐 들어가는 방법이 있을 것이다. 샘플링은 Nginx 에서 리버스 프록싱할 때 이미 다른 네임스페이스의 Gateway Svc 를 호출하고 있는 바이다. [KubeProxy 가 모니터링하여 생성한 IpTables 에서 Service 이름에 맞는 Pod 의 EndPoint 를 찾아가고 있다고 보면 된다.] 하나는 Cluster 내부 DNS 에서 이름주는 방식을 이용한 방법이고, 하나는 Service 의 외부 Domain 을 리턴하는 ExternalName 의 특성을 이용한 방법.
- Nginx 의 Reverse Proxy 를 타고 들어와 SpringCloud Gateway 에서 Predicate 에 잡혀 uri 을 통해 쏘게 된다.
샘플 자원인 Gateway 의 application.yaml
spring:
application:
name: sample-gateway
cloud:
gateway:
routes:
- id: service-a
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
server:
port: 8080
management:
endpoints:
web:
exposure:
include:
- gateway
- health
gateway:
enable: true
---
spring:
config:
activate:
on-profile: k8s
cloud:
gateway:
routes:
- id: sample-service
uri: http://admin:8080/
predicates:
- Path=/api/versionNumber/admin/**
filters:
- RewritePath=/api/versionNumber/admin/(?<path>.*), /api/versionNumber/$\{path}
외부 -> K8s Cluster Ingress Controller -> Nginx Svc [리버스 프록싱] -> Gateway Pod [ 정확히는 Iptables 에 연결된 Svc 의 Pod 엔드포인트] -> 포워딩 -> Sample App