'전체 글'에 해당되는 글 357건
- 2020.08.22 :: 운영체제 - 디스크 사용량 및 정보 확인
- 2020.08.19 :: Elasticsearch - 퍼포먼스 튜닝하는 방법 by ebay 1
- 2020.08.17 :: Gradle - Could not initialize class org.codehaus.groovy.runtime.InvokerHelper(Spring, gradle project)
- 2020.08.16 :: MongoDB - 백업하고 복구하기(mongodump&mongorestore) 1
- 2020.08.15 :: Kubernetes - 볼륨(Volume),퍼시스턴트 볼륨&볼륨 클레임(persistent volume&claim)
- 2020.08.02 :: Kubernetes - ingress nginx 설치 및 사용법 3
- 2020.08.02 :: Git - 체리픽(Cherry-pick), 다른 브랜치 혹은 다른 레포지토리의 커밋 가져오기
- 2020.07.30 :: Java - 자바 날짜&시간 java.time 패키지(LocalDateTime, ZoneDateTime)
디스크의 남은 용량 확인
- df -k : 킬로바이트 단위로 현재 남은 용량을 확인
- df -m : 메가바이트 단위로 남은 용량을 왁인
- df -h : 보기 좋게 보여줌
- df . : 현재 디렉토리가 포함된 파티션의 남은 용량을 확인
> df -h
Filesystem Size Used Avail Capacity iused ifree %iused Mounted on
/dev/disk1s1 466Gi 10Gi 379Gi 3% 487648 4881965192 0% /
devfs 190Ki 190Ki 0Bi 100% 659 0 100% /dev
/dev/disk1s2 466Gi 73Gi 379Gi 17% 1142296 4881310544 0% /System/Volumes/Data
/dev/disk1s5 466Gi 2.0Gi 379Gi 1% 2 4882452838 0% /private/var/vm
map auto_home 0Bi 0Bi 0Bi 100% 0 0 100% /System/Volumes/Data/home
현재 디렉토리의 용량 확인
- du -a : 현재 디렉토리의 사용량을 파일단위 출력
- du -s : 총 사용량을 확인
- du -h : 보기 좋게 바꿔줌
- du -sh * : 한단계 서브디렉토리 기준으로 보여준다.
> du -sh *
4.0K Dockerfile
4.0K HELP.md
12K README.md
34M build
4.0K build.gradle
60K gradle
8.0K gradlew
4.0K gradlew.bat
284K kube-logging
48K kube-resource
4.0K kube-sample.iml
24K nginx
4.0K settings.gradle
4.0K src
'인프라 > 운영체제' 카테고리의 다른 글
운영체제 - 상호배제와 동기화(뮤텍스,TAS,세마포어,모니터) (0) | 2019.07.28 |
---|---|
운영체제 - 병행 프로세스란? (0) | 2019.07.28 |
운영체제 - 쓰레드란?(Thread,사용자 수준 쓰레드, 커널 수준 쓰레드, 혼합형 쓰레드) (1) | 2019.07.27 |
운영체제 - 프로세스(Process)란? 프로세스상태,Context Switching (0) | 2019.07.27 |
운영체제 - 컴퓨터 하드웨어의 구성(CPU,RAM 등) (0) | 2019.07.22 |
'Search-Engine > Elasticsearch&Solr' 카테고리의 다른 글
그래들 프로젝트를 사용중인데, 간혹 idea에서 "Could not initialize class org.codehaus.groovy.runtime.InvokerHelper"라는 에러 메시지가 뜨는 경우가 있다. 여러가지 요인이 있을 수 있지만, 필자가 저 에러를 보았던 순간은 jdk 1.8에서 jdk14 버전으로 올리면서 났던 에러 메시지 인데, 이유는 gradle version이 jdk14를 지원하지 못하는 낮은 버전이었기 때문이다.
그래서, 프로젝트 디렉토리중 "gradle/wrapper/gradle-wrapper.properties"에서 gradle 버전을 올려주어서 해결하였다.(5.x -> 6,3)
'Web > Gradle' 카테고리의 다른 글
Gradle - 캐시된 dependencies 라이브러리 삭제하기 (0) | 2020.06.23 |
---|---|
Gradle - Gradle 간단히 Task 작성하는 방법 (0) | 2019.10.21 |
Gradle - Gradle로 자바 프로젝트 만들기 (0) | 2019.10.20 |
Gradle - Gradle의 기본 (0) | 2019.10.20 |
이번에 다루어볼 내용은 몽고디비에서 데이터를 백업하고 복구하는 방법이다.
백업하기(덤프, dump)
몽고디비가 설치되어 있다면, mongodump라는 명령어로 몽고디비 데이터를 백업할 수 있다.
> mongodump --host 127.0.0.1 --port 27017
위 명령으로 데이터를 백업한다면, 현재 디렉토리에 /dump 디렉토리가 생기고 이 디렉토리 밑에 데이터가 복구되어 있다.(DB 별로 폴더가 생겨있고, 그 폴더안에 BSON으로 데이터가 백업되어 있다.)
> mongodump --out ~/mongo_backup --host 127.0.0.1 --port 27017
--out 옵션으로 데이터 백업의 디렉토리 위치를 정해줄 수 있다.
> mongodump --out <dump data path> --host 127.0.0.1 --port 27017 -u <username> -p <password>
username/password로 인증이 필요하다면, 위 명령어로 백업이 가능하다.
> mongodump --out <dump data path> --host 127.0.0.1 --port 27017 -u <username> -p <password> --db <덤프할 db명>
만약 몽고디비에서 특정 데이터베이스만 백업하고 싶다면, 위처럼 --db 옵션을 이용하면 된다.
> mongodump --out <dump data path> --host <dbhost> --port 27017 -u <username> -p <password> --db <dbname> --collection <collectionName>
특정 컬렉션 단위까지 세분화하여 백업하려면 --collection 옵션을 이용한다. 만약 mongodump&mongorestore에서 "error connecting to db server: server returned error on SASL authentication step:Authentication failed" 에러가 났다면, 아래와 같이 옵션하나를 넣어준다.
--authenticationDatabase admin
복구하기(restore)
다음은 위에서 덤프한 데이터를 복구하는 방법이다. 복구는 mongorestore라는 명령어를 이용한다.
> mongorestore --host 127.0.0.1 --port 27017 \
-u <username> -p <password> --drop <drop db name> \
--db <복구할 db name> <복구할 덤프데이터가 있는 디렉토리>
--drop 옵션은 복구전에 드랍시킬 데이터베이스 명을 입력하면 된다.(복구 전 원래 데이터베이스를 드랍시키고 백업 데이터로 새로 복구하는 것이다.) 위 명령을 간단하게 작성해보면 아래와 같다.
> mongorestore --host 127.0.0.1 --port 27017 --db local /mongo_backup/local
특정 데이터베이스를 복구하고 싶다면, 덤프 데이터가 있는 디렉토리에서 특정 데이터베이스의 디렉토리를 명시해야한다. 만약 모든 데이터를 전부다 백업하고 싶다면 아래 명령어를 입력한다.
> mongorestore --host 127.0.0.1 --port 27017 <dump data가 있는 디렉토리>
컬렉션 단위로 리스토어하기 위해서는 --collection 옵션을 사용하며, collectionName.bson까지 백업데이터 경로를 명시해주어야한다.
> mongorestore --host <dbhost> --port 27017 --db <dbname> --collection <collectionName> <data-dump-path/dbname/collection.bson> --drop
ex)
> mongorestore --port 27017 --db test2 --collection rest2 /mydata/restoredata/test/restaurants.bson --drop
여기까지 간단하게 몽고디비 데이터 백업 및 복구에 대해 다루어보았다.
'Database > MongoDB' 카테고리의 다른 글
DB - MongoDB 맵리듀스(Map Reduce) (0) | 2019.09.21 |
---|---|
DB - MongoDB FindAndModify 란? (0) | 2019.09.19 |
DB - MongoDB OperationFailed Sort operation used more than the maximum 33554432 bytes of Ram. (0) | 2019.09.19 |
DB - MongoDB Insert Ordered 옵션은? (0) | 2019.09.19 |
DB - MongoDB Text Search(본문 검색) (0) | 2019.09.16 |
오늘은 쿠버네티스의 볼륨에 대해 다루어 볼 것이다. 간단하게 몇가지 볼륨 플러그인에 대해 예제를 다루어보고, 퍼시스턴트 볼륨&볼륨 클레임에 대해 다루어본다.
emptyDir
해당 플러그인은 pod가 실행되는 호스트의 디스크를 임시로 컨테이너에 볼륨으로 할당해서 사용하는 방법이다. pod가 사라지면 emptyDir에 마운트해서 사용하는 데이터도 모두 사라진다. 하지만, pod이 종료되지 않고 단순히 container만 재시작된 것이라면 데이터는 유지된다. 주로 대용량 데이터 계산의 중간 연산 저장 용도로 사용한다.
apiVersion: v1
kind: Pod
metadata:
name: springboot-web
spec:
containers:
- name: springboot-web
image: 1223yys/springboot-web:0.1.6
ports:
- containerPort: 8080
volumeMounts:
- mountPath: /emptyDir
name: emptyDir_vol
volumes:
- name: emptyDir_vol
emptyDir: {}
hostPath
pod가 실행된 호스트의 파일이나 디렉터리를 pod에 마운트한다. emptyDir가 임시 디렉터리를 마운트하는 것이라면, hostPath는 호스트에 있는 실제 파일이나 디렉터리를 마운트하는 것이며 pod를 재시작하더라도 데이터가 보존된다. 보통은 /var/lib/docker 같은 도커 시스템용 디렉토리를 컨테이너에 마운트해 시스템 모니터링 등을 진행할 때 사용하기도 한다.
apiVersion: v1
kind: Pod
metadata:
name: springboot-web
spec:
containers:
- name: springboot-web
image: 1223yys/springboot-web:0.1.6
ports:
- containerPort: 8080
volumeMounts:
- mountPath: /test-volume
name: hostPath-vol
volumes:
- name: hostPath-vol
hostPath:
path: /tmp
type: Directory
실제로 볼륨이 잘 마운트 되었는지 확인해보자.
> kubectl exec <pod-name> -it sh
> cd /test-volume
> touch test.txt
> exit
> ls /tmp
호스트의 /tmp 디렉토리에 test.txt가 생성되었다면, 볼륨이 잘 마운트 된 것이다. hostPath 볼륨에는 아래와 같이 여러가지 타입이 존재한다.
- null: hostPath 볼륨을 마운트하기 전에 아무것도 확인하지 않는다.
- DirectoryOrCreate: 설정한 경로에 디렉터리가 없으면 퍼미션이 755인 빈 디렉터리를 만든다.
- Directory: 설정한 경로에 디렉터리가 존재해야한다. 호스트에 해당 디렉터리가 없으면 파드는 ContainerCreating 상태로 남고 생성이 안된다.
- FileOrCreate: 설정한 경로에 파일이 없으면 퍼미션이 644인 빈 파일을 만든다.
- File: Directory와 동일
- Socket: 설정한 경로에 유닉스 소켓 파일이 있어야한다.
퍼시스턴트 볼륨&볼륨 클레임(Persistent Volume, Persistent Volume Claim)
쿠버네티스에서 볼륨을 사용하는 구조는 PV와 PVC로 분리되어있다. PV는 볼륨 자체를 뜻하고 클러스터 안에서 자원으로 다룬다. 파드하고는 별개로 관리되고 별도의 생명주기가 있다.
PVC는 사용자가 PV에 하는 요청이다. 사용하고 싶은 용량은 얼마인지, 읽기/쓰기는 어떤 모드를 사용하고 싶은지 등을 정하여 요청한다. 즉, 쿠버네티스는 이처럼 파드에 볼륨을 직접 할당하는 형태가 아니라, 중간에 PVC를 두어 파드와 파드가 사용할 스토리지를 분리하는 전략인 것이다. 이렇게 분리됨으로써 이점은 다양한 스토리지를 PV로 사용할 수 있는데, 파드는 어떠한 스토리지의 볼륨인지 신경쓸 필요없이 PVC으로 요청만 하면 되기 때문에 의존성이 줄어들게 되고, manifest도 분리됨으로써 설정파일 자체의 복잡함이 사라진다.
PV&PVC 생명주기
- Provisioning: PV를 만드는 단계를 뜻한다.
- static provisioning: 미리 PV를 만들어 두고 사용자의 요청이 있으면 미리 만들어둔 PV를 할당한다.(보통 스토리지 용량의 제한이 있을때 사용한다.)
- dynamic provisioning: 사용자가 PVC를 거쳐 PV를 요청했을 때, PV를 생성해 제공한다.
- Binding: 바인딩은 프로비저닝으로 만든 PV를 PVC와 연결하는 단계이다. PVC에서 원하는 스토리지의 용량과 접근 방법을 명시해서 요쳥하면 맞는 PV가 할당된다. 이때 PVC에서 원하는 PV가 없다면 요청은 실패하고, PVC에서 원하는 PV가 생길 때까지 대기하다가 PVC에 바인딩된다.(PVC 하나에 여러 PV가 매핑될 수 없다.)
- Using: PVC는 파드에 설정되고 파드는 PVC를 볼륨으로 인식해서 사용한다. 할당된 PVC는 파드를 유지하는 동안 계속 사용하며 시스템에서 임의로 삭제할 수 없다. 이 기능을 "Storage Object In Use Protection"이라 한다.
- Reclaiming: 사용이 끝난 PVC는 삭제되고 PVC를 사용하던 PV를 초기화하는 과정을 뜻한다. 초기화 정책으로는 아래와 같다.
- Retain: PV를 그대로 보존한다. PVC가 삭제되면 사용 중이던 PV는 해제(released)상태라서 아직 다른 PVC가 재사용할 수 없다.(데이터는 아직 그대로 보존되어있다.) 만약 해당 PV를 재사용하려면 아래와 같은 순서로 직접 초기화해줘야한다.
- PV삭제. 만약 PV가 외부 스토리지와 연결되어있다면 PV는 삭제되더라도 외부 스토리지의 볼륨은 그대로 남아있다.
- 외부 스토리지에 남은 데이터를 직접 정리한다.
- 남은 스토리지의 볼륨을 삭제하거나 재사용하려면 해당 볼륨을 이용하는 PV를 다시 만든다.
- Delete: PV를 삭제하고 연결된 외부 스토리지 쪽의 볼륨도 삭제한다. 동적 프로비저닝은 기본적으로 해당 정책을 따른다.
- Recycle: PV의 데이터들을 삭제하고 다시 새로운 PVC에서 PV를 사용할 수 있도록한다.
- Retain: PV를 그대로 보존한다. PVC가 삭제되면 사용 중이던 PV는 해제(released)상태라서 아직 다른 PVC가 재사용할 수 없다.(데이터는 아직 그대로 보존되어있다.) 만약 해당 PV를 재사용하려면 아래와 같은 순서로 직접 초기화해줘야한다.
이제 실제로 실습을 통해 알아보자. 다음은 퍼시스턴트 볼륨 템플릿이다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: persistent-volume
namespace: levi-volume
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
storageClassName: manual
persistentVolumeReclaimPolicy: Delete
hostPath:
path: /tmp
간단하게 설정파일에 작성된 설정을 설명하면 아래와 같다.
- accessModes
- ReadWriteOne: 노드 하나에만 볼륨을 읽기/쓰기하도록 마운트한다.
- ReadOnlyMany: 여러 개 노드의 읽기 전용으로 마운트한다.
- ReadWriteMany: 여러 개 노드에서 읽기/쓰기를 허용하도록 마운트한다.
- storageClassName: 스토리지 클래스를 설정하는 필드이고, PVC가 특정 스토리지 클래스를 명시하여 요청하면 해당 스토리지 클래스로 선언된 PV와 연결된다. 만약 스토리지 클래스를 설정하지 않았다면, 특정 스토리지 클래스를 명시하지 않은 PVC가 요청하면 매핑된다.
- persistentVolumeReclaimPolicy: PV가 해제되었을 때의 초기화 옵션을 설정한다.(Retain/Recycle/Delete)
- .spec.hostPath: 해당 PV의 볼륨 플러그인을 명시한다.
볼륨이 잘 생성되었는지 확인해보자.
> kubectl apply -f ./kube-resource/persistent-volume-sample.yaml
> kubectl get pvc -n levi-volume
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistent-volume 1Gi RWO Delete Available manual
STATUS는 아래와 같이 4가지의 상태값을 갖는다.
- Available: PVC에서 사용할 수 있는 상태
- Bound: 특정 PVC에 연결된 상태
- Released: PVC는 삭제되었고, PV는 아직 초기화되지 않은 상태
- Failed: 자동 초기화를 실패한 상태
다음은 퍼시스턴트 볼륨 클레임 설정이다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: persistent-volume-claim
namespace: levi-volume-claim
spec:
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
storageClassName: manual
resources:
requests:
storage: 500Mi
다른 내용은 볼륨의 설정과 크게 차이가 없고 한가지만 설명하자면, ".spec.resources.requests.storage"는 자원을 얼마나 사용할 것인지 명시하는 것이며, PV의 용량보다 높다면, 할당되지 않고 Pending 상태가 된다.
볼륨 클레임이 잘 생성되었는지 확인해보자.
> kubectl apply -f ./kube-resource/persistent-volume-claim-sample.yaml
> kubectl get pvc -n levi-volume-claim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistent-volume-claim Bound persistent-volume 1Gi RWO manual 11s
> kubectl get pv -n levi-volume
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistent-volume 1Gi RWO Delete Bound levi-volume-claim/persistent-volume-claim manual 4h44m
PVC와 PV가 binding 된 이후에는 각각 STATUS가 Bound 상태로 변경되었다. 보통 PV와 PVC를 연동할때는 storageClassName을 보고 연결되는데, 또 다른 방법으로는 label로 연결할 수도 있다.
#볼륨
apiVersion: v1
kind: PersistentVolume
metadata:
name: persistent-volume
namespace: levi-volume
labels:
location: local
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
storageClassName: manual
persistentVolumeReclaimPolicy: Delete
hostPath:
path: /tmp
#볼륨 클레임
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: persistent-volume-claim
namespace: levi-volume-claim
spec:
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
storageClassName: manual
resources:
requests:
storage: 500Mi
selector:
matchLabels:
location: local
마지막으로 pod에 볼륨을 마운트해보자.
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample-deployment
spec:
replicas: 2
selector:
matchLabels:
app: springboot-web
template:
metadata:
labels:
app: springboot-web
spec:
containers:
- name: springboot-web
image: 1223yys/springboot-web:0.2.5
imagePullPolicy: Always
ports:
- containerPort: 8080
volumeMounts:
- mountPath: /test-volume
name: persistent-volume
livenessProbe:
httpGet:
port: 9090
path: /api
initialDelaySeconds: 60
readinessProbe:
httpGet:
port: 9090
path: /api
initialDelaySeconds: 60
volumes:
- name: persistent-volume
persistentVolumeClaim:
claimName: persistent-volume-claim
만약 예제대로 따라왔다면, pod은 뜨지 못하고 pending된 상태로 머물러 있을 것이다. 왜냐하면 클러스터는 클레임을 사용하는 pod와 동일한 네임스페이스에 있어야하기 때문이다. 위 deployment는 네임스페이스가 default이므로, 볼륨 클레임을 default 네임스페이스에 하나 생성해주어야 한다.
여기까지 정말 간단하게 쿠버네티스 볼륨과 볼륨 클레임에 대해 다루어보았다. 사실 다루어볼 볼륨 플러그인이 아주 많기 때문에 다음 포스팅에서 더 자세히 다루어볼 것이다.
'인프라 > Docker&Kubernetes' 카테고리의 다른 글
Kubernetes - kustomize를 이용한 쿠버네티스 오브젝트 관리 (0) | 2020.08.30 |
---|---|
Kubernetes - 쿠버네티스 클러스터 로깅(logging, fluentd + kafka + elk) (0) | 2020.08.24 |
Kubernetes - ingress nginx 설치 및 사용법 (3) | 2020.08.02 |
Kubernetes - kubernetes(쿠버네티스) resources(cpu/momory) 할당 및 관리 (0) | 2020.07.19 |
Docker - 도커 이미지 만들기 ! (Dockerfile) (0) | 2020.07.18 |
이번 포스팅에서 다루어볼 내용은 간단하게 쿠버네티스 ingress-nginx를 설치하고, 외부 트래픽을 내부 팟에게 전달해주는 예제이다. 바로 예제로 들어간다.
> git clone https://github.com/kubernetes/ingress-nginx.git
> cd ./ingress-nginx/deploy/static/provider/baremetal
> kubectl apply -f .
> kubectl get deploy -n ingress-nginx
NAME READY UP-TO-DATE AVAILABLE AGE
ingress-nginx-controller 1/1 1 1 60s
여기까지 따라왔다면 설치는 완료되었고, ingress-nginx를 위한 서비스 등이 떴을 것이다.
kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.97.27.106 <none> 80:30431/TCP,443:31327/TCP 4m35s
30431로 접속해보자.
> curl levi.local.com:30431
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.19.1</center>
</body>
</html>
호스트 설정을 통해 levi.local.com을 localhost로 포워딩하도록 설정하였다. 실습에 localhost는 사용하기 힘들기 때문에 etc/hosts 설정을 통해 로컬을 특정 도메인처럼 할당해보자.
> sudo vi /etc/hosts
127.0.0.1 levi.local.com
이제 ingress-nginx가 포워딩할 웹어플리케이션 팟을 띄워보자.
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample-deployment
spec:
replicas: 1
selector:
matchLabels:
app: springboot-web
template:
metadata:
labels:
app: springboot-web
spec:
containers:
- name: springboot-web
image: 1223yys/springboot-web:0.2.5
imagePullPolicy: Always
ports:
- containerPort: 8080
livenessProbe:
httpGet:
port: 8080
path: /api
initialDelaySeconds: 60
readinessProbe:
httpGet:
port: 8080
path: /api
initialDelaySeconds: 60
디플로이먼트 컨트롤러로 springboot-web 애플리케이션 팟을 관리하도록 매니페스트를 작성하였다. 해당 매니패스트를 적용해보자.
> kubectl apply -f deployment.yaml
다음으로는 springboot-web으로 접근할 수 있게 해주는 외부 통로인 서비스를 작성해보자.
apiVersion: v1
kind: Service
metadata:
name: springboot-web-service
spec:
selector:
app: springboot-web
ports:
- name: http
port: 80
targetPort: 8080
해당 서비스도 배포해보자.
> kubectl apply -f service.yaml
마지막으로 ingress nginx 매니페스트 파일을 작성해보자.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-ingress-sample
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: levi.local.com
http:
paths:
- path: /
backend:
serviceName: springboot-web-service
servicePort: 80
위 매니페스트 파일에서 host를 유의하자. 포스팅 초반에 localhost를 로컬 도메인으로 할당해야한다 했는데, 그 이유가 위 host 때문이다. 위에 보이는 host는 실제로 http 요청이 들어올때 요청 헤어의 "Host : levi.local.com" 을 참조하기 때문이다.
이제 요청을 보내보자.
> curl http://levi.local.com:30431/api
new api !
ingress-nginx를 통해 웹앱 팟에 잘 접근되는 것을 확인할 수 있다.
'인프라 > Docker&Kubernetes' 카테고리의 다른 글
Kubernetes - 쿠버네티스 클러스터 로깅(logging, fluentd + kafka + elk) (0) | 2020.08.24 |
---|---|
Kubernetes - 볼륨(Volume),퍼시스턴트 볼륨&볼륨 클레임(persistent volume&claim) (0) | 2020.08.15 |
Kubernetes - kubernetes(쿠버네티스) resources(cpu/momory) 할당 및 관리 (0) | 2020.07.19 |
Docker - 도커 이미지 만들기 ! (Dockerfile) (0) | 2020.07.18 |
Docker - 도커를 이용해 Single node Kafka 띄우기 (0) | 2020.06.04 |
오늘 다루어볼 내용은 git의 cherry-pick(체리픽)이다. 보통 체리픽을 사용하는 이유는 다른 브랜치의 커밋의 일부만 가져올때 많이 사용한다. 간단하게 예제를 다루어보자.
> gb
* [1] feature/add-function
[2] feature/update-function
위처럼 두개의 브랜치가 존재하고 각 브랜치의 커밋로그는 아래와 같다.
> feature/add-function
* 13e321a - (HEAD -> feature/add-function) add c.txt (9 minutes ago) <levi.yoon>
* f894b25 - add b.txt (9 minutes ago) <levi.yoon>
* 35402a1 - add a.txt (9 minutes ago) <levi.yoon>
> feature/update-function
* cb6a172 - (HEAD -> feature/update-function) add d.txt (7 minutes ago) <levi.yoon>
* d516ac8 - update a.txt (7 minutes ago) <levi.yoon>
* 13e321a - (feature/add-function) add c.txt (9 minutes ago) <levi.yoon>
* f894b25 - add b.txt (10 minutes ago) <levi.yoon>
* 35402a1 - add a.txt (10 minutes ago) <levi.yoon>
여기서 feature/update-function 브랜치에 있는 "d516ac8 - update a.txt" 이 커밋내용만 가져와 feature/add-function 브랜치에 넣고 싶을 때 cherry-pick을 이용한다.
> git checkout feature/add-function
> git cherry-pick d516ac8
> git log
* 09edc77 - (HEAD -> feature/add-function) update a.txt (46 seconds ago) <levi.yoon>
* 13e321a - add c.txt (12 minutes ago) <levi.yoon>
* f894b25 - add b.txt (12 minutes ago) <levi.yoon>
* 35402a1 - add a.txt (12 minutes ago) <levi.yoon>
지금 한 예제는 같은 레포지토리의 다른 브랜치에서 커밋을 가져왔지만, 다른 원격 레포지토리의 특정 브랜치에 있는 커밋내용도 내 레포지토리 브랜치에 넣을 수 있다.
'Tools > Git&GitHub' 카테고리의 다른 글
Git - git reset, revert란(깃 커밋 되돌리기)? (0) | 2020.06.06 |
---|---|
Git - git rebase란? Merge & Rebase 차이점 (0) | 2020.03.27 |
Git - 자주 사용되거나 유용한 Git 명령들 (0) | 2020.03.27 |
Git - .gitignore가 작동하지 않을때(.gitignore가 안먹을때) (0) | 2020.03.26 |
Git - 특정 브랜치(branch)만 clone하는 명령어 (0) | 2019.10.15 |
오늘 다루어볼 내용은 jdk1.8의 날짜&시간을 다루는 java.time 패키지를 다루어볼 것이다. 바로 java.time 패키지 내용을 다루기 전에 우선 프로그래밍에서의 날짜와 시간에 대해 표준인 ISO-8601에 대해 먼저 알아본다.
ISO-8601
<wiki>
ISO 8601 Data elements and interchange formats - Information interchange - Representation of dates and times은 날짜와 시간과 관련된 데이터 교환을 다루는 국제 표준이다. 이 표준은 국제 표준화 기구(ISO)에 의해 공포되었으며 1988년에 처음으로 공개되었다. 이 표준의 목적은 날짜와 시간을 표현함에 있어 명백하고 잘 정의된 방법을 제공함으로써, 날짜와 시간의 숫자 표현에 대한 오해를 줄이고자함에 있는데, 숫자로 된 날짜와 시간 작성에 있어 다른 관례를 가진 나라들간의 데이터가 오갈때 특히 그렇다.
일반적으로, ISO 8601는 그레고리력 (proleptic Gregorian도 가능)에서의 날짜와 (부가적으로 시간대 정보를 포함하는) 24시간제에 기반하는 시간, 시간 간격(time interval) 그리고 그들의 조합에 대한 표현과 형식에 적용된다. 이 표준은 표현할 날짜/시간 요소에 어떠한 특정 의미도 할당하지 않는다; 그 의미는 사용 맥락에 따라 달라질 것이다. 추가로, 표현될 날짜와 시간은 표준 내에서의 지정된 의미의 숫자(예를 들자면, 중국 달력의 년도 이름)가 아니고서는 단어를 포함할 수 없으며 단어들은 문자(예: 이미지, 소리)를 사용하지 않는다.
교환을 위한 표현에서, 날짜와 시간은 재배치되어서, 가장 큰 시간 용어(년도)가 왼쪽에 놓이며 각각의 더 작은 용어들은 이전 용어의 우측에 놓이게 된다. 표현은 아라비아 숫자와 표준 내에서 특정 의미를 제공하는 ("-", ":", "T", "W" 그리고 "Z"와 같은) 어떤 문자들로 작성되어야 한다. 그것이 의미하는 바는, "January" 혹은 "Thursday"처럼 날짜의 일부를 작성하는 어떤 평범한 방법이 교환 표현에서는 허용되지 않는다는 것이다.
참조 : https://ko.wikipedia.org/wiki/ISO_8601
위 내용은 위키 내용을 인용하였는데, 한마디로 세계에서 각기 다른 시간대에서 사용하기 위한 날짜&시간에 대한 표준을 정해놓은 것이다. 포맷으로는 보통 아래와 같은 포맷을 다룬다.(물론 기본형식이 있지만 아래 확장 형식을 대부분 사용하는 듯하다.)
- 날짜(년월일) : YYYY-MM-DD
- 날짜(년월) : YYYY-MM
- 날짜&시간 : YYYY-MM-DDThh:mm:ss(YYYY-MM-DDThh:mm:ss.sss)
- 시간 : hh:mm:ss(hh:mm:ss.sss)
날짜와 시간을 다루는데 아주 중요한 것중 하나는 "표준 시간대 지정자"이다. ISO-8601의 표준 시간대는 (불특정 위치의) "지역 시간"(local time), "UTC" 혹은 "UTC의 오프셋"으로 표현된다.
만약 UTC 관계 정보에 시간 표현이 함께 주어지지 않는다면, 시간은 지역 시간으로 간주된다. 동일한 시간대에서 통신 시 지역 시간을 가정하는 것이 가장 안전할지 몰라도, 시간대가 다른(국가와 국가간 시차) 지역간의 통신에서 지역시간을 사용하는 경우는 아주 모호하게 된다.
(UTC 오프셋이 표현되지 않는다면 해당 시간이 어느나라 기준의 시간인지 알수 없기에 각자 나라의 시간대로 표현이 불가능하다.)
UTC
시간이 UTC인 경우, 시간 뒤에 빈칸없이 "Z" 를 직접 추가해야 한다. Z는 오프셋이 0인 UTC를 위한 지역 지정자이다. 그러므로 "09:30:12"의 UTC는 "09:30:12Z"로 표현된다.
UTC에서의 시간 오프셋
UTC에서의 오프셋은 위에서 Z를 붙였던 것과 동일한 방법으로 시간뒤에 덧붙인다. 우리나라는 기준시보다 9시간이 빠른 나라이기 때문에 시간을 UTC 오프셋으로 표현하게 되면 "09:30:12+09:00"으로 표현하게 된다. 이렇게 UTC 오프셋으로 시간을 표현하게 되면 기준시에 오프셋이 붙어있는 것이기 때문에 해당 시간으로 다른 시간대의 나라의 시간으로도 표현이 명확히 가능하게 된다.(느린 시간대라면 -로 오프셋을 표현한다.)
java.time 패키지의 핵심 클래스
날짜와 시간을 하나로 표현하는 Calendar클래스와 달리, java.time 패키지에서는 날짜와 시간을 별도의 클래스로 분리해 놓았다. 시간을 표현할 때는 LocalTime 클래스를 사용하고, 날짜를 표현할 때는 LocalDate클래스를 사용한다. 그리고 날짜와 시간이 모두 필요할 때는 LocalDateTime클래스를 사용하면 된다. 만약 여기에 Time-Zone까지 다뤄야 한다면, ZonedDateTime클래스를 사용한다.
LocalDateTime은 기본적으로 Time-zone이 없는 형태이다. 그 말은 ZoneOffset을 표기 하지 않는 날짜 형식을 출력해준다. 물론 ZoneOffset을 고려해서 LocalDateTime 오브젝트를 만들수 있다. 하지만 Time-zone이 없는 개념이기 때문에, 아래와 같이 출력이 된다.
LocalDateTime.now() - 2020-07-30T00:38:55.215245
ZonedDateTime.now() - 2020-07-30T00:38:55.215245+09:00[Asia/Seoul]
Jdk1.8 이전의 날짜&시간을 다루는 Calendar는 ZonedDateTime처럼, 날짜와 시간 그리고 시간대까지 모두 가지고 있다. Date와 유사한 클래스로는 Instant가 있는데, 이 클래스는 날짜와 시간을 초 단위(엄밀히는 나노초까지)로 표현한다. 날짜와 시간을 초단위로 표현한 값을 time stamp라고 부르는데, 이 값은 날짜와 시간을 하나의 정수로 표현할 수 있으므로 날짜와 시간의 차이를 계산하거나 순서를 비교하는데 유리해서 데이터 베이스에서 많이 사용한다.(타임스탬프는 타임존에 대한 정보없이 절대적인 시간을 다루기 때문에 아주 명확한 값을 가지게 된다.) 이외에도 날짜를 더 세부적으로 다룰 수 있는 Year, YearMonth, MonthDay와 같은 클래스도 있다.
예제 코드는 millisecond는 조금 다르지만 같은 시간이라 가정하자.
LocalDateTime.now()
-> 2020-07-30T01:04:38.488822
LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()), ZoneId.systemDefault())
-> 2020-07-30T01:04:38.490
LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()), ZoneId.of("Asia/Seoul"))
-> 2020-07-30T01:04:38.490
LocalDateTime.ofInstant(Instant.ofEpochSecond(System.currentTimeMillis() / 1000), ZoneId.systemDefault())
-> 2020-07-30T01:04:38
LocalDateTime.ofInstant(Instant.ofEpochSecond(System.currentTimeMillis() / 1000), ZoneId.of("Asia/Seoul"))
-> 2020-07-30T01:04:38
LocalDateTime.ofInstant(Instant.ofEpochSecond(System.currentTimeMillis() / 1000), ZoneId.systemDefault()).plusDays(7)
-> 2020-08-06T01:04:38
LocalDateTime.ofEpochSecond(System.currentTimeMillis() / 1000, 0, ZoneOffset.UTC)
-> 2020-07-29T16:04:38
LocalDateTime.ofEpochSecond(System.currentTimeMillis() / 1000, 0, ZoneOffset.ofHours(9))
-> 2020-07-30T01:04:38
ZonedDateTime.now()
-> 2020-07-30T01:04:38.491441+09:00[Asia/Seoul]
LocalDateTime.now().atZone(ZoneId.of("Asia/Seoul"))
-> 2020-07-30T01:04:38.491568+09:00[Asia/Seoul]
작성중...
'프로그래밍언어 > Java&Servlet' 카테고리의 다른 글
Java - Reactor switchIfEmpty 사용시 주의점(Lambda, 람다 Lazy Evaluation) (2) | 2020.07.29 |
---|---|
Java - 자바 클로져(Closure) & 커링(currying) (0) | 2020.07.22 |
Java - jdk 14 record(레코드) 란?! Data class(데이터 클래스) (0) | 2020.05.14 |
Java - Model(Object) mapping을 위한 Mapstruct (맵스트럭트)! (0) | 2020.04.29 |
Java - 중첩이 많은 Stream 처리 Tip ! (0) | 2020.04.21 |