[JAVA] 간단한 .java 파일을 실행 가능한 .jar 파일로 만들고 도커 이미지로 쿠버네티스 cronjob 생성하기
어제 간단한 특정시간이 지난 뒤 프로세스가 종료되는 자바파일을 구현해야하는 상황이 생겼다.
그래서 jar 파일을 만들고 Dockerfile을 작성한 뒤 Docker image를 생성하고,
쿠버네티스 cronjob 에서 가져오는 것 까지해보았다.
먼저 .jar 파일은 그동안 maven, gradle을 통해 자동으로 쭈루룩 만들어줬었는데,
그냥 test.java main 메소드 하나 있는 것을 컴파일하여 나온 .class 파일과 매니패스트파일을 합치는 작업을 해주면 된다.
근데 이때 매니패스트 지정하는데에 시행착오를 겪었다..
(문자열 끝에 엔터가 필요하다고...)
test.java
가장먼저 개발을 할 test.java 를 만들자.
간단히 Thread를 통해 3초의 대기를 준 뒤 프로세스가 종료되게 하였다.
import java.text.SimpleDateFormat;
import java.util.Calendar;
class joon_test {
public static void main(String args[]) throws InterruptedException {
System.out.println("Metanet java run...");
int num = 3 ;
// time output format
SimpleDateFormat fmt = new SimpleDateFormat("HH:mm:ss");
for(int i=0; i < num; i++){
Calendar cal = Calendar.getInstance() ;
// print (now times + i )
System.out.println("Metanet ... " + fmt.format(cal.getTime()) + "=" + i) ;
// 1sec sleeping
Thread.sleep(1000);
}
System.out.println("Metanet java stop.");
}
}
manifest.txt
매니패스트파일에선 .jar가 실행될 때 메인이 되는 클래스가 무엇인지 정의해 준다.
(꼭 라인 끝에 enter를 쳐서 1줄을 띄어 줘야한다. 안 그럼 에러가 난다..)
Main-class: joon_test
컴파일 & 합치기
제일 먼저 .java 를 컴파일하여 .class를 생성하자. (파일은 클래스명.class 가 생성된다)
$ javac .\test.java
아래 명령어로 매니패스트와 클래스파일을 합쳐주자.
(이때 옵션의 mf 순서에 따라 뒤에 인자를 맞춰주어야함 매니패스트파일과 클래스파일)
$ jar -cvmf manifest.txt joon.jar joon_test.class
이제 .jar가 생성되었다.아래 명령어로 기동해보자.
$ java -jar joon.jar
Metanet java run...
Metanet ... 12:49:40=0
Metanet ... 12:49:41=1
Metanet ... 12:49:42=2
Metanet java stop.
Dockerfile 작성
이제 docker image 생성을 위한 파일을 작성해야한다.
가벼운 openjdk alpine 이미지를 가져와 실행하도록 했다.
FROM openjdk:17-alpine
LABEL maintainer="Lim joon hyeok"
LABEL title="job sample image"
LABEL version="1.0"
LABEL description="sampling"
COPY ./joon.jar /
# Start the service
CMD ["java","-jar","/joon.jar"]
Image Build & Push
위 Dockerfile을 빌드하자
$ docker build -t job-test:1.0 .
[+] Building 21.4s (7/7) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 269B 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/openjdk:17-alpine 3.7s
=> [internal] load build context 0.1s
=> => transferring context: 1.31kB 0.0s
=> [1/2] FROM docker.io/library/openjdk:17-alpine@sha256:4b6abae565492dbe9e7a894137c966a7485154238902f2f25e9dbd9784383d81 16.2s
=> => resolve docker.io/library/openjdk:17-alpine@sha256:4b6abae565492dbe9e7a894137c966a7485154238902f2f25e9dbd9784383d81 0.1s
=> => sha256:53c9466125e464fed5626bde7b7a0f91aab09905f0a07e9ad4e930ae72e0fc63 928.44kB / 928.44kB 0.8s
=> => sha256:d8d715783b80cab158f5bf9726bcada5265c1624b64ca2bb46f42f94998d4662 186.80MB / 186.80MB 9.7s
=> => sha256:4b6abae565492dbe9e7a894137c966a7485154238902f2f25e9dbd9784383d81 319B / 319B 0.0s
=> => sha256:a996cdcc040704ec6badaf5fecf1e144c096e00231a29188596c784bcf858d05 951B / 951B 0.0s
=> => sha256:264c9bdce361556ba6e685e401662648358980c01151c3d977f0fdf77f7c26ab 3.48kB / 3.48kB 0.0s
=> => sha256:5843afab387455b37944e709ee8c78d7520df80f8d01cf7f861aae63beeddb6b 2.81MB / 2.81MB 0.5s
=> => extracting sha256:5843afab387455b37944e709ee8c78d7520df80f8d01cf7f861aae63beeddb6b 0.6s
=> => extracting sha256:53c9466125e464fed5626bde7b7a0f91aab09905f0a07e9ad4e930ae72e0fc63 0.3s
=> => extracting sha256:d8d715783b80cab158f5bf9726bcada5265c1624b64ca2bb46f42f94998d4662 6.2s
=> [2/2] COPY ./mtp.jar / 0.9s
=> exporting to image 0.2s
=> => exporting layers 0.1s
=> => writing image sha256:f4884aad8840a6bc1fd5acd9578d391d1d09d14428f4788835b4ddcfd0d63fbc 0.0s
=> => naming to docker.io/library/joon-test:1.0
생성된 이미지 조회
$ docker images joon-test
REPOSITORY TAG IMAGE ID CREATED SIZE
joon-test 1.0 f4884aad8840 About a minute ago 326MB
이제 생성한 이미지를 원격 레지스트리 서버에 올려야한다.
필자는 Naver Container Registry 를 사용중이다.
docker tag 명령어로 각자 레지스트리 도메인을 가지는 이미지로 변경 한 뒤
docker login 후에 원격컨테이너레지스트리에 푸쉬하자.
(임시로 url을 기입하였습니다.)
$ registry_url=joon95-sample-url.kr.ncr.ntruss.com
$ docker tag mtp-job-test:0.1 $registry_url/job-test:1.0
$ docker login -u 유저네임 $registry_url
password: 패스워드입력
$ docker push $registry_url/job-test:1.0
secret
쿠버네티스에서 private image에 접근하기 위한 secret을 생성해야한다.
생성될 namespace는 default에 생성하였다.
$ kubectl create secret docker-registry 생성할secret명 \
--docker-server=컨테이너레지스트리URL
--docker-username=유저명
--docker-password=패스워드
-n default
cronjob
이제 1분마다 자동으로 실행될 크론잡을 작성하자.
apiVersion: batch/v1
kind: CronJob
metadata:
namespace: default
name: sample-cronjob
spec:
schedule: "*/1 * * * *"
concurrencyPolicy: "Forbid"
#startingDeadlineSeconds: 200
#suspend: false
successfulJobsHistoryLimit: 2
failedJobsHistoryLimit: 1
jobTemplate:
spec:
backoffLimit: 0
template:
metadata:
labels:
parent: "sample-cronjob"
spec:
imagePullSecrets:
- name: 생성한시크릿명
containers:
- command: ["java", "-jar", "joon.jar"]
image: 컨테이너레지스트리서버URL/job-test:1.0
imagePullPolicy: Always
name: job-sample
resources: {}
dnsPolicy: ClusterFirst
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
restartPolicy: Never
$ kubectl apply -f cronjob.yaml -n default
이제 1분마다 job pod가 기동됩니다. 로그를 보면 정상적으로 출력되고요 ㅎㅎ
현재시간을 stoud log 찍은 뒤 3초 후 종료(Completed) 되는 프로세스가 완료되었습니다.
이전에 포스팅했던 3scale 백업 고도화에서 dockerfile / cronjob 에 대한 설정을 미리 검토해놓아서 쉽게 적용할 수 있었습니다.