도커 레지스트리에는 로그인 기능이 없기 때문에 Nginx의 기본인증 기능을 사용해야하며, HTTP 프로토콜에는 인증을 지원하지 않습니다. 따라서 HTTPS 프로토콜을 사용해야합니다.
/etc/hosts 파일 수정
# vi /etc/hosts
127.0.0.1 localhost
127.0.1.1 master
192.168.0.201 registry.example.com
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
사설 SSL 인증서 생성 및 설정
# openssl genrsa -out server.key 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
.......................................+++++
..................................+++++
e is 65537 (0x010001)
사설 SSL 인증서 키 파일 설정
# openssl req -new -key server.key -out server.csr
Can't load /root/.rnd into RNG
139832043372992:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/root/.rnd
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:KO
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:Seoul
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Company
Organizational Unit Name (eg, section) []:Company
Common Name (e.g. server FQDN or YOUR name) []:registry.example.com
Email Address []:jinsu@example.com
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Country Name : 국가 코드이며, 대문자 KO를 입력합니다.
State or Province Name : 주 또는 도입니다. 상황에 맞게 입력
Locality Name : 도시입니다. 상황에 맞게 입력
Organization Name : 회사이름 입력
Organizational Unit Name : 회사 조직 이름 입력
Common Name : Docker 레지스트리를 실행하는 서버의 도메인 주소 /etc/hosts 파일에 설정한대로 입력
Email Address : 이메일 주소 입력
서버 인증서 파일을 생성하고 시스템에 설치
# openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
Signature ok
subject=C = KO, ST = Some-State, L = Seoul, O = Company, OU = Company, CN = registry.example.com, emailAddress = jinsu@example.com
Getting Private key
# ls
total 68
-rw-r--r-- 1 root root 1350 Oct 30 07:23 server.crt
-rw-r--r-- 1 root root 1070 Oct 30 07:23 server.csr
-rw------- 1 root root 1675 Oct 30 07:05 server.key
# cp server.crt /usr/share/ca-certificates/
# echo "server.crt" | tee -a /etc/ca-certificates.conf
server.crt
# update-ca-certificates
Updating certificates in /etc/ssl/certs...
rehash: warning: skipping ca-certificates.crt,it does not contain exactly one certificate or CRL
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
# service docker restart
보통은 알려진 CA에서 발급한 TLS 인증서를 사용하여 레지스트리를 보호하는 것이 매우 권장되지만, 자체 서명된 인증서를 사용하거나 암호화되지 않은 HTTP 연결을 통해 레지스트리를 사용하도록 선택할 수 있습니다. 만약 인터넷이 안되거나 그냥 개인이 따로 저장하겠다고하면 HTTP 연결을 통해 개인 저장소를 구축할 수 있습니다.
# docker run -d -p 5000:5000 --name hello-registry \
> -v /tmp/registry:/tmp/registry \
> registry
2976da0da8d746a1ba60f88930dd46bff8143795c516dd9653fcf1a6942432e5
#
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2976da0da8d7 registry "/entrypoint.sh /etc…" 3 seconds ago Up 2 seconds 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp hello-registry
이미지 파일은 호스트의 /tmp/registry 디렉터리에 저장됩니다.
이미지 tag, push 설정하기
# docker build --tag hello:0.1 .
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello 0.1 75f0625417ad 36 seconds ago 232MB
# docker tag hello:0.1 localhost:5000/hello:0.1
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello 0.1 75f0625417ad About a minute ago 232MB
localhost:5000/hello 0.1 75f0625417ad About a minute ago 232MB
# docker push localhost:5000/hello:0.1
The push refers to repository [localhost:5000/hello]
2d1630f34efb: Pushed
0492b535da40: Pushed
1db69eba3ab3: Pushed
b9b64cd744c9: Pushed
83109fa660b2: Pushed
30d3c4334a23: Pushed
f2fa9f4cf8fd: Pushed
0.1: digest: sha256:6b8296113e568e6184eb893fd02f006034c26b0778c86922cd65554f0f379106 size: 1782
가지고있는 Dockerfile을 build 하여 name : hello | tag : 0.1로 image 생성
tag 생성 : docker tag <이미지이름>:<tag> <registry URL>/<이미지 이름>:<tag>
push 명령 : docker push <registry URL>/<이미지 이름>:<tag>
개인저장소에 있는 이미지를 Pull 받을 서버
# docker pull 192.168.0.201:5000/hello:0.1
Error response from daemon: Get "https://192.168.0.201:5000/v2/": http: server gave HTTP response to HTTPS client
pull 명령으로 개인저장소 서버에서 이미지를 받아오는데 오류 로그 발생
해결방법 : insecure-registries 설정 필요
# vi /etc/docker/daemon.json
{
"insecure-registries" : ["192.168.0.201:5000"]
}
# docker pull 192.168.0.201:5000/hello:0.1
0.1: Pulling from hello
2e6e20c8e2e6: Pull complete
0551a797c01d: Pull complete
512123a864da: Pull complete
0cde67eab025: Pull complete
119857f951bc: Pull complete
41786f85cd57: Pull complete
56705ad25a7d: Pull complete
Digest: sha256:6b8296113e568e6184eb893fd02f006034c26b0778c86922cd65554f0f379106
Status: Downloaded newer image for 192.168.0.201:5000/hello:0.1
192.168.0.201:5000/hello:0.1
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
192.168.0.201:5000/hello 0.1 75f0625417ad 14 minutes ago 232MB
# docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: djwlstn12345
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
만약 위 방법으로 안된다면 docker logout 후 다시 login 해보시길 바랍니다.
시크릿은 암호, 토큰 또는 키와 같은 소량의 중요한 데이터를 포함하는 오브젝트이다. 이를 사용하지 않으면 중요한 정보가 파드 명세나 컨테이너 이미지에 포함될 수 있다. 시크릿을 사용한다는 것은 사용자의 기밀 데이터를 애플리케이션 코드에 넣을 필요가 없음을 뜻한다.
시크릿은 시크릿을 사용하는 파드와 독립적으로 생성될 수 있기 때문에, 파드를 생성하고, 확인하고, 수정하는 워크플로우 동안 시크릿(그리고 데이터)이 노출되는 것에 대한 위험을 경감시킬 수 있다. 쿠버네티스 및 클러스터에서 실행되는 애플리케이션은 비밀 데이터를 비휘발성 저장소에 쓰는 것을 피하는 것과 같이, 시크릿에 대해 추가 예방 조치를 취할 수도 있다.
시크릿은 컨피그맵과 유사하지만 특별히 기밀 데이터를 보관하기 위한 것이다.
주의
쿠버네티스 시크릿은 기본적으로 API 서버의 기본 데이터 저장소(etcd)에 암호화되지 않은 상태로 저장된다. API 접근(access) 권한이 있는 모든 사용자 또는 etcd에 접근할 수 있는 모든 사용자는 시크릿을 조회하거나 수정할 수 있다. 또한 네임스페이스에서 파드를 생성할 권한이 있는 사람은 누구나 해당 접근을 사용하여 해당 네임스페이스의 모든 시크릿을 읽을 수 있다. 여기에는 디플로이먼트 생성 기능과 같은 간접 접근이 포함된다.
1. 시크릿을 안전하게 사용하려면 최소한 다음의 단계를 따르는 것이 좋다.
2. 시크릿에 대해 저장된 데이터 암호화(Encryption at Rest)를 활성화한다.시크릿 읽기 및 쓰기를 제한하는 RBAC 규칙을 활성화 또는 구성한다.
3. 파드 생성 권한을 가진 사람은 암묵적으로 시크릿에 접근할 수 있음에 주의한다.적절한 경우, RBAC과 같은 메커니즘을 사용하여 새로운 시크릿을 생성하거나 기존 시크릿을 대체할 수 있는 주체(principal)들을 제한한다.
시크릿의 사용
파드가 시크릿을 사용하는 주요한 방법으로 다음의 세 가지가 있다.
하나 이상의 컨테이너에 마운트된 볼륨 내의 파일로써 사용.
컨테이너 환경 변수로써 사용.
파드의 이미지를 가져올 때 kubelet에 의해 사용.
쿠버네티스 컨트롤 플레인 또한 시크릿을 사용한다. 예를 들어, 부트스트랩 토큰 시크릿은 노드 등록을 자동화하는 데 도움을 주는 메커니즘이다
Secret 사용
# secret 배포 YAML 파일 생성 (암호화 base64 지원)
jinsu@jinsu:~$ cat secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
jinsu@jinsu:~$
# secret 생성 및 확인
jinsu@jinsu:~$ kubectl apply -f secret.yaml
secret/mysecret created
jinsu@jinsu:~$
jinsu@jinsu:~$ kubectl get secret
NAME TYPE DATA AGE
default-token-vm2hl kubernetes.io/service-account-token 3 8h
mysecret Opaque 2 5s
jinsu@jinsu:~$
# secret 상세정보
jinsu@jinsu:~$ kubectl get secret mysecret -oyaml
apiVersion: v1
data:
password: MWYyZDFlMmU2N2Rm
username: YWRtaW4=
kind: Secret
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"password":"MWYyZDFlMmU2N2Rm","username":"YWRtaW4="},"kind":"Secret","metadata":{"annotations":{},"name":"mysecret","namespace":"default"},"type":"Opaque"}
creationTimestamp: "2022-08-14T09:37:57Z"
name: mysecret
namespace: default
resourceVersion: "19863"
selfLink: /api/v1/namespaces/default/secrets/mysecret
uid: 221983f8-81b7-4e62-bd7d-736b88dcdba8
type: Opaque
jinsu@jinsu:~$
# 같은 secret2를 생성 (암호화하지 않음)
jinsu@jinsu:~$ cat secret2.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysecret2
type: Opaque
stringData:
username: admin
password: password
jinsu@jinsu:~$
# secret2 생성 및 확인
jinsu@jinsu:~$ kubectl apply -f secret2.yaml
secret/mysecret2 created
jinsu@jinsu:~$ kubectl get secret
NAME TYPE DATA AGE
default-token-vm2hl kubernetes.io/service-account-token 3 8h
mysecret Opaque 2 6m39s
mysecret2 Opaque 2 4s
jinsu@jinsu:~$
# secret2 상세정보를 보면 계정과 패스워드가 자동 암호화되는것을 볼 수 있다.
jinsu@jinsu:~$ kubectl get secret mysecret2 -oyaml
apiVersion: v1
data:
password: cGFzc3dvcmQ=
username: YWRtaW4=
kind: Secret
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Secret","metadata":{"annotations":{},"name":"mysecret2","namespace":"default"},"stringData":{"password":"password","username":"admin"},"type":"Opaque"}
creationTimestamp: "2022-08-14T09:44:32Z"
name: mysecret2
namespace: default
resourceVersion: "20151"
selfLink: /api/v1/namespaces/default/secrets/mysecret2
uid: dec64c7d-f910-4e3d-8da3-0317118a5fa1
type: Opaque
jinsu@jinsu:~$
하나의 크론잡 오브젝트는 크론탭 (크론 테이블) 파일의 한 줄과 같다. 크론잡은 잡을 크론 형식으로 쓰여진 주어진 일정에 따라 주기적으로 동작시킨다.
주의 모든 크론잡 일정: 시간은 kube-controller-manager의 시간대를 기준으로 한다.
컨트롤 플레인이 파드 또는 베어 컨테이너에서 kube-controller-manager를 실행하는 경우, kube-controller-manager 컨테이너에 설정된 시간대는 크론잡 컨트롤러가 사용하는 시간대로 결정한다.
주의 v1 CronJob API은 위에서 설명한 타임존 설정을 공식적으로 지원하지는 않는다.
CRON_TZ 또는 TZ 와 같은 변수를 설정하는 것은 쿠버네티스 프로젝트에서 공식적으로 지원하지는 않는다. CRON_TZ 또는 TZ 와 같은 변수를 설정하는 것은 크론탭을 파싱하고 다음 잡 생성 시간을 계산하는 내부 라이브러리의 구현 상세사항이다. 프로덕션 클러스터에서는 사용을 권장하지 않는다. 크론잡 리소스에 대한 매니페스트를 생성할 때에는 제공하는 이름이 유효한 DNS 서브도메인 이름이어야 한다. 이름은 52자 이하여야 한다. 이는 크론잡 컨트롤러는 제공된 잡 이름에 11자를 자동으로 추가하고, 작업 이름의 최대 길이는 63자라는 제약 조건이 있기 때문이다.
크론잡
크론잡은 백업, 리포트 생성 등의 정기적 작업을 수행하기 위해 사용된다. 각 작업은 무기한 반복되도록 구성해야 한다(예: 1일/1주/1달마다 1회). 작업을 시작해야 하는 해당 간격 내 특정 시점을 정의할 수 있다.
동작 방식
CronJob -> Job 생성
CronJob 사용
# Cronjob 배포 YAML 파일 생성
jinsu@jinsu:~$ cat cronjob.yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
schedule: "* * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: print-date
image: busybox
args: ["date"]
restartPolicy: OnFailure
jinsu@jinsu:~$
# Cronjob 생성 및 확인
jinsu@jinsu:~$ kubectl apply -f cronjob.yaml
cronjob.batch/hello created
jinsu@jinsu:~$ kubectl get cronjob
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
hello * * * * * False 0 <none> 17s
jinsu@jinsu:~$
# Cronjob으로 인한 job 생성 확인
jinsu@jinsu:~$ kubectl get job
NAME COMPLETIONS DURATION AGE
hello-1660469460 0/1 3s 3s
jinsu@jinsu:~$
jinsu@jinsu:~$ kubectl get job
NAME COMPLETIONS DURATION AGE
hello-1660469460 1/1 4s 74s
hello-1660469520 1/1 4s 14s
jinsu@jinsu:~$
jinsu@jinsu:~$ kubectl get job
NAME COMPLETIONS DURATION AGE
hello-1660469460 1/1 4s 2m45s
hello-1660469520 1/1 4s 105s
hello-1660469580 1/1 4s 45s
jinsu@jinsu:~$
CronJob 삭제
# CronJob 삭제
jinsu@jinsu:~$ kubectl delete cronjob hello
cronjob.batch "hello" deleted
jinsu@jinsu:~$
# 삭제확인
jinsu@jinsu:~$ kubectl get cronjob
No resources found in default namespace.
jinsu@jinsu:~$
jinsu@jinsu:~$ kubectl get job
No resources found in default namespace.
jinsu@jinsu:~$
잡에서 하나 이상의 파드를 생성하고 지정된 수의 파드가 성공적으로 종료될 때까지 계속해서 파드의 실행을 재시도한다. 파드가 성공적으로 완료되면, 성공적으로 완료된 잡을 추적한다. 지정된 수의 성공 완료에 도달하면, 작업(즉, 잡)이 완료된다. 잡을 삭제하면 잡이 생성한 파드가 정리된다. 작업을 일시 중지하면 작업이 다시 재개될 때까지 활성 파드가 삭제된다.
간단한 사례는 잡 오브젝트를 하나 생성해서 파드 하나를 안정적으로 실행하고 완료하는 것이다. 첫 번째 파드가 실패 또는 삭제된 경우(예로는 노드 하드웨어의 실패 또는 노드 재부팅) 잡 오브젝트는 새로운 파드를 기동시킨다.
잡을 사용하면 여러 파드를 병렬로 실행할 수도 있다.
잡을 스케줄에 따라 구동하고 싶은 경우(단일 작업이든, 여러 작업의 병렬 수행이든), 크론잡(CronJob)을 참고한다.
- 1회성 배치작업(데이터수집, 분석, 기계학습)
잡(Job) 사용
# job 배포 YAML 파일 생성 (date 기능)
jinsu@jinsu:~$ cat job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: myfirstjob
spec:
template:
spec:
containers:
- name: print-date
image: busybox
args: ["date"]
restartPolicy: OnFailure
backoffLimit: 2
jinsu@jinsu:~$
# job 배보
jinsu@jinsu:~$ kubectl apply -f job.yaml
job.batch/myfirstjob created
jinsu@jinsu:~$
# job 상태 확인
jinsu@jinsu:~$ kubectl get job
NAME COMPLETIONS DURATION AGE
myfirstjob 1/1 5s 5s
jinsu@jinsu:~$
# job pod 확인
jinsu@jinsu:~$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myfirstjob-bl5w8 0/1 Completed 0 16s
jinsu@jinsu:~$
# job 상세정보 (restartPolicy: OnFailure) job 기능 상태확인
jinsu@jinsu:~$ kubectl get job myfirstjob -oyaml
apiVersion: batch/v1
kind: Job
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"batch/v1","kind":"Job","metadata":{"annotations":{},"name":"myfirstjob","namespace":"default"},"spec":{"backoffLimit":2,"template":{"spec":{"containers":[{"args":["date"],"image":"busybox","name":"print-date"}],"restartPolicy":"OnFailure"}}}}
creationTimestamp: "2022-08-14T09:12:55Z"
labels:
controller-uid: 102804d2-35af-451e-8e52-b428b626a35d
job-name: myfirstjob
name: myfirstjob
namespace: default
resourceVersion: "18588"
selfLink: /apis/batch/v1/namespaces/default/jobs/myfirstjob
uid: 102804d2-35af-451e-8e52-b428b626a35d
spec:
backoffLimit: 2
completions: 1
parallelism: 1
selector:
matchLabels:
controller-uid: 102804d2-35af-451e-8e52-b428b626a35d
template:
metadata:
creationTimestamp: null
labels:
controller-uid: 102804d2-35af-451e-8e52-b428b626a35d
job-name: myfirstjob
spec:
containers:
- args:
- date
image: busybox
imagePullPolicy: Always
name: print-date
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: OnFailure
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
status:
completionTime: "2022-08-14T09:13:00Z"
conditions:
- lastProbeTime: "2022-08-14T09:13:00Z"
lastTransitionTime: "2022-08-14T09:13:00Z"
status: "True"
type: Complete
startTime: "2022-08-14T09:12:55Z"
succeeded: 1
jinsu@jinsu:~$
# job으로 생성한 date 기능 확인
jinsu@jinsu:~$ kubectl logs myfirstjob-bl5w8
Sun Aug 14 09:12:59 UTC 2022
jinsu@jinsu:~$
# job 생성할때 date2로 생성
jinsu@jinsu:~$ cat job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: myfirstjob
spec:
template:
spec:
containers:
- name: print-date
image: busybox
args: ["date2"]
restartPolicy: OnFailure
backoffLimit: 2
jinsu@jinsu:~$
# job 생성 및 확인
jinsu@jinsu:~$ kubectl apply -f job.yaml
job.batch/myfirstjob created
jinsu@jinsu:~$
jinsu@jinsu:~$ kubectl get job
NAME COMPLETIONS DURATION AGE
myfirstjob 0/1 3s 3s
jinsu@jinsu:~$
# pod에 문제가 생겨 RESTARTS 카운트가 올라가 2회 발생시 Pod가 자동 삭제 확인
jinsu@jinsu:~$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myfirstjob-bl2lb 0/1 RunContainerError 0 8s
jinsu@jinsu:~$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myfirstjob-bl2lb 0/1 Terminating 2 36s
jinsu@jinsu:~$
잡(Job) 삭제
# 현재 job 상태
jinsu@jinsu:~$ kubectl get job
NAME COMPLETIONS DURATION AGE
myfirstjob 0/1 4m40s 4m40s
# job 삭제
jinsu@jinsu:~$ kubectl delete job myfirstjob
job.batch "myfirstjob" deleted
jinsu@jinsu:~$
jinsu@jinsu:~$ kubectl get job
No resources found in default namespace.
jinsu@jinsu:~$
# 현재 데몬셋 확인
jinsu@jinsu:~$ kubectl get daemonset
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
myapp 1 1 1 1 1 <none> 6m24s
# 데몬셋 삭제
jinsu@jinsu:~$ kubectl delete daemonset myapp
daemonset.apps "myapp" deleted
jinsu@jinsu:~$
jinsu@jinsu:~$ kubectl get daemonset
No resources found in default namespace.
jinsu@jinsu:~$ dd