STATUS가 Available인거 확인
create순서는 pv pvc rs 순서..
구조를 그려보면 이렇다
pvc와 pv가 bound상태에서 pv를 지우려고하면 안지워진다
pvc를 먼저 지워야함
그리고 재사용은 안됨
x 곱하기 표시는 몇 번 시도했는지 임
이제 삭제하고 마무리..
만약 pv를 먼저 지우게 되면 포그라운드에서 멈취있게 된다.
터미널 하나 더 열어서 pvc를 지우면 그제야 지워지게 됨
동적 프로비저닝 개념을 그려보면
vagrant halt로 VM을 먼저 중지시키고
Vagrant.configure("2") do |config|
# Define VM
config.vm.define "k8s-node1" do |ubuntu|
ubuntu.vm.box = "ubuntu/focal64"
ubuntu.vm.hostname = "k8s-node1"
ubuntu.vm.network "private_network", ip: "192.168.100.100"
ubuntu.vm.provider "virtualbox" do |vb|
vb.name = "k8s-node1"
vb.cpus = 2
vb.memory = 4000
unless File.exist?('./.disk/ceph1.vdi')
vb.customize ['createmedium', 'disk', '--filename', './.disk/ceph1.vdi', '--size', 10240]
end
vb.customize ['storageattach', :id, '--storagectl', 'SCSI', '--port', 2, '--device', 0, '--type', 'hdd', '--medium', './.disk/ceph1.vdi']
end
end
config.vm.define "k8s-node2" do |ubuntu|
ubuntu.vm.box = "ubuntu/focal64"
ubuntu.vm.hostname = "k8s-node2"
ubuntu.vm.network "private_network", ip: "192.168.100.101"
ubuntu.vm.provider "virtualbox" do |vb|
vb.name = "k8s-node2"
vb.cpus = 2
vb.memory = 3000
unless File.exist?('./.disk/ceph2.vdi')
vb.customize ['createmedium', 'disk', '--filename', './.disk/ceph2.vdi', '--size', 10240]
end
vb.customize ['storageattach', :id, '--storagectl', 'SCSI', '--port', 2, '--device', 0, '--type', 'hdd', '--medium', './.disk/ceph2.vdi']
end
end
config.vm.define "k8s-node3" do |ubuntu|
ubuntu.vm.box = "ubuntu/focal64"
ubuntu.vm.hostname = "k8s-node3"
ubuntu.vm.network "private_network", ip: "192.168.100.102"
ubuntu.vm.provider "virtualbox" do |vb|
vb.name = "k8s-node3"
vb.cpus = 2
vb.memory = 3000
unless File.exist?('./.disk/ceph3.vdi')
vb.customize ['createmedium', 'disk', '--filename', './.disk/ceph3.vdi', '--size', 10240]
end
vb.customize ['storageattach', :id, '--storagectl', 'SCSI', '--port', 2, '--device', 0, '--type', 'hdd', '--medium', './.disk/ceph3.vdi']
end
end
# config.vm.define "k8s-docker" do |ubuntu|
# ubuntu.vm.box = "ubuntu/focal64"
# ubuntu.vm.hostname = "k8s-docker"
# ubuntu.vm.network "private_network", ip: "192.168.100.103"
# ubuntu.vm.provider "virtualbox" do |vb|
# vb.name = "k8s-docker"
# vb.cpus = 2
# vb.memory = 2000
# end
# end
config.vm.provision "shell", inline: <<-SHELL
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config
sed -i 's/archive.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list
sed -i 's/security.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list
systemctl restart ssh
SHELL
end
### File이 존재하느냐 현재 디렉토리의 .disk의 ceph1.vdi라는 파일이
### 없으면 만들고 있으면 넘어가라는 말임
### 그리고 storageattatch해서 스토리지를 연결해주는 것임
이런 구성을 하면
이런식으로 10G짜리 디스크가 할당되는 것임
vagrant snapshot save before-rook #혹시 모르니 snapshot을 찍어둬도 좋음
vagrant up
ceph는 스토리지고 스토리지를 쿠버네티스에 맞게 설치해주는 것이 rook임
전통적으로는 스토리지를 사게되면 Block
File
object
를 따로.. 사게됨
근데 ceph는 하나의 관리자로 file block object스토리지를 관리하게됨
knode1으로 ssh 접속을 하고..
ansible all -i ~/kubespray/inventory/mycluster/inventory.ini -m command -a 'sudo lsblk'
https://rook.io/docs/rook/latest/Getting-Started/quickstart/
git clone --single-branch --branch v1.9.3 https://github.com/rook/rook.git
cd rook/deploy/examlples
ls
kubectl create -f crds.yaml -f common.yaml -f operater.yaml
kubectl get ns
kubectl get po rook-ceph
### 이게 running상태로 완료가 되어야함
rook-ceph-operator는 설치를 위한것이지 rook-ceph가 아님
cluster를 설치 할껀데.. 종류가 많다
kubectl create -f cluster.yaml
### operater에 의해서 pod들이 쭉쭉만들어짐
필수 구성은 이렇다
구성 실패..
다시 동적 프로비저닝을 해보면
https://kubernetes.io/ko/docs/concepts/storage/storage-classes/
spec이 없는 대표적인 구성임
실습은 나중에..
https://kubernetes.io/ko/docs/concepts/storage/storage-classes/
apiVersion apps/v1
kind: Deployment
metadata:
name: myweb-deploy
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: myweb
image: ghcr.io/c1t1d0s7/go-myweb
ports:
- containerPort: 8080
kubectl create -f myweb-deploy.yaml
kubectl get deploy
kubectl get rs
kubectl get po
Deploy가 RS을 만들고 RS가 Pod를 만듬
https://thenewstack.io/deployment-strategies/
어플리케이션의 배포 전략이라는 문서임
소프트웨어를 어떻게 배포할것인지에 대한 일반적인 전략
어플리케이션을 배포할 때 6가지 전략
이것은 쿠버네티스에서만 사용하는 전략은 아니고 일반적인 전략임
그중에서 우리는 Deployment에 대해서 사용할 예정
이 상태에서 2번째 버전을 어떻게 배포할것이냐에 대한 문제이다.
이렇게 하는 방식이 Recreate 전략이다.
다만 이 전략의 단점은 downtime이 발생한다는 것이다.
다음은 Rolling Update이다
하나 지우고 하나 생성하는 방식으로 변경을 한다.
이 전략의 장점은 무중단시스템이라는 것이다.
단점은 Rollout과 rollback에 시간이 좀 걸린다.
API는 기본적으로 V1과 V2를 지원할 수 있어야한다.
다음은 Blue/Green이다.
이거는 먼저 V2를 프로비저닝 해놓고
장점은 다운타임이 적지만
단점은 여유 리소스가 필요하다. 그래서 비용이 많이 든다.
다음은 Canary 배포이다.
카나리아라는 새가 있다.
유해 가스에 민감한 새를 미리 보내고 살아 돌아오면 안전하다라는 식이었음
일부 트래픽을 V2로 천천히 보내고 점점 V2로 모든 트래픽을 보낸다.
쿠버네티스는 이런 기능을 가지고 있지는 않다
다음은 A/B테스팅이다.
예를들어 pc에서 들어갈 때랑 스마트폰으로 들어갈 때랑의 차이를 생각해보면 된다.
즉, 특정 종류 일부만 테스트를 한다.
다음은 Shadow이다.
트래픽이 양쪽 다 전송됨 클라이언트는 구분하지 못함
다 좋지만
단점은 구현하기가 어렵다.
표로 정리해보면...
go-myweb:v1을 go-myweb:v1.0으로 바꾸어주어야함
이 두 개 서비스를 만듬
apiVersion: v1
kind: Service
metadata:
name: myweb-svc-lb
spec:
type: LoadBalancer
selector:
app: web
ports:
- port: 80
targetPort: 8080
apiVersion: apps/v1
kind: Deployment
metadata:
name: myweb-deploy
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: myweb
image: ghcr.io/c1t1d0s7/go-myweb:v1.0
ports:
- containerPort: 8080
이렇게하고 둘 다 create해줌
### 한 쪽은
watch -n1 kubectl get deploy,rs,po
### 또 한 쪽은
watch -n1 curl 192.168.100.240
kubectl rollout
kubectl rollout status deploy myweb-deploy
kubectl rollout history deploy myweb-deploy
이미지를 교체하는 방법
replace
kubectl edit
apply
patch
kubectl set
도 있음
kubectl set image deployments myweb-deploy myweb=ghcr.i
o/c1t1d0s7/go-myweb:v2.0 --record
### --record옵션을 붙이면 kubectl rollout history 했을 때 추적이 가능해진다.
kubectl rollout
kubectl rollout undo deploy myweb-deploy
1상태로 돌아가지만 버전은 1,2를 지나서 3임
또 바꿀 수 있다
apiVersion: apps/v1
kind: Deployment
metadata:
name: myweb-deploy
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: myweb
image: ghcr.io/c1t1d0s7/go-myweb:v2.0
ports:
- containerPort: 8080
kubectl replace -f myweb-deploy.yaml
이런식으로 하면 이미지를 무슨 이미지를 사용했는지 history에 남지 않기 때문에 다른 방식으로 해보겠다.
annotations를 추가하면 된다.
annotations:
kubernetes.io/change-cause: "Change Go Myweb version from 3 to 4"
kubectl apply -f myweb-deploy.yaml
이런식으로 중간 과정을 확인할 수도 있다.
아직더 Deployment를 할 수 있음
https://kubernetes.io/ko/docs/concepts/workloads/controllers/deployment/#%EC%A0%84%EB%9E%B5
max surge와
max unavailable이 있는데
surge는 무언가 평소보다 튀는 값을 말함
Deployment가 있고 rollout을 할 때
max surge가 1이면 순간적으로 3+1해서 4개까지할 수 있다고 한다.
만들고 지운다는 개념으로 하면 최대 3개를 넘게되고
지우고만든다라는 개념으로 하게되면 최대 3개를 넘을 필요는 없어지게된다.
이런식으로 배포 전략을 짤 수 있다고 한다.
비율로 선언할 수 있고 값으로 선언할 수 있다 비율로 할 시에는 올림을 한다고 한다.
이런식으로 작성을 할 수 있다.
만약에 특정 버전의 세부정보를 확인하기 위해서는 이렇게 하면 된다.
이렇게 이미지 히스토리의 버전을 지정해서 돌아갈 수 있다.
minReadySeconds로 딜레이를 일부러 시킬 수 있다.
liveness나
startup probe를 사용한다면 필요하지 않을 수 있다.
revisionHistoryLimit
히스토리를 제한할 수도 있다고 한다.
데몬셋에서도 updateStrategy가 있다.
역시 데몬셋 updateStrategy에도 maxUnavailable이 있다.
stateless한 구성은 전부 이런 디플로이먼트로 한다고 한다..
https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
sudo systemctl restart nfs-fernel-server
sudo mkdir /nfsvolume
sudo rm -f #안에 파일이 있다면 다 지워줌
git clone https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner.git
ls
cd nfs-subdir[tab]
ls
cd deploy
ls
vi deployment.yaml
### 생성
kubectl create -f deployment.yaml
kubectl create -f class.yaml
### 확인
kubectl get deploy,rs,po
kubectl get sc
cd yaml/volume/nfs
ls
cd ..
cd -r nfs nfs-dynamic
cd nfs-dynamic
ls
rm mypv.yaml
### 그리고 이미 생성되었던 서비스들 delete
vi mvpvc.yaml
kubectl get sc
이렇게 pvc만 만들어도 pvclass에 의해서 자동으로 만들어지는 것을 볼 수 있다.
sudo ls -l /nfsvolume
kubectl create -f mypvc-dynamic.yaml -f myweb-rs.yaml -f myweb-svc-lb.yaml
결과 확인
삭제
kubectl delete -f .
kubectl get pv,pvc
kubectl get dc
pvc를 지우면 pv가 같이 지워지는 것을 확인할 수 있다.
cd ~/nfs-subdir-external-provisioner
vi class.yaml
annotations:
storageclass.kubernetes.io/is-default-class: "true"
이 부분이 달라졌다고 함
이렇게 다른점 확인
이름 뒤에 default가 붙는 것을 default 스토리지 클래스라고 한다.
mkdir env
cd env
vi myweb.yaml
apiVersion: v1
kind: Pod
metadata:
name: myweb-env
spec:
containers:
- name: myweb
image: ghcr.io/c1t1d0s7/go-myweb:alpine
env:
- name: MESSAGE
value: "Customized Hello World"
kubectl create -f myweb.yaml
kubectl get po
kubectl exec -it myweb-env -- sh
이렇게 환경변수를 제공할 수 있다는 것을 기억하길 바람..
kubectl delete -f .
cd ~
mkdir cm
cd cm
kubectl api-resources | grep configmap
kubectl explain cm.spec
kubectl explain cm
kubectl explain cm.data
https://kubernetes.io/ko/docs/concepts/configuration/configmap/
사용 용도:
사용동기도 한 번 읽어보는 것을 추천함
vi myconfig.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mymessage
data:
MESSAGE: Customized Hello ConfigMap
kubectl create -f mymessage.yaml
kubectl get cm
kubectl describe cm mymessage
configmap의 이름을 설정할 수 있다고 함..
apiVersion: v1
kind: Pod
metadata:
name: myweb-env
spec:
containers:
- name: myweb
image: ghcr.io/c1t1d0s7/go-myweb:alpine
envFrom:
- configMapRef:
name: mymessage
kubectl create -f myweb.yalm
kubectl exec -it myweb-env -- sh
이렇게 키 값 형태로 환경변수를 설정할 수 있다.
kubectl explain pods.spec.volumes.configMap
### 볼륨으로 지정하는게 가능하다
apiVersion: v1
kind: Pod
metadata:
name: myweb-cm-vol
spec:
containers:
- name: myweb
image: ghcr.io/c1t1d0s7/go-myweb:alpine
volumeMounts:
- name: cmvol
mountPath: /myvol
volumes:
- name: cmvol
configMap:
name: mymessage
kubectl create....
kubectl exec -it myweb-cm-vol -- sh
ls
cd /myvol/
ls
ls -l
cat MESSAGE
### 키가 file명 데이터가 value
이런 것도 있다
1번은 모두를 등록
2번은 특정 키 값만 등록
기본적인 원리는 똑같다 key value를 제공하기 때문임
다만 다른점은 value를 base64로 인코딩을 함
중요한거는 base64는 암호화가 아님
물론 AWS의 KMS를 이용하거나
Hashicop의 Vault를 이용하면 암호화가 가능하긴하다.
cd ~
mkdir secret
cd secret
kubectl api-resources | secret
kubectl explain secret.type
value의 종류를 지정해줄 수 있다.
dockercfg
dockerconfigjson은 docker login할 때 그 인증임
이걸 이용해서 registry를 이용할 수 있는것임
vi mydata.yaml
apiVersion: v1
kind: Secret
metadata:
name: mydata
type: Opaque
data:
id: admin
pwd: P@ssw0rd
kubectl create -f mydata.yaml
하면 오류가남
base64
admin
이런식으로 인코딩해서 값을 집어넣어준다.
apiVersion: v1
kind: Secret
metadata:
name: mydata
type: Opaque
data:
id: YWRtaW4K
pwd: UEBzc3cwcmQK
kubectl create -f mydata.yaml
kubectl get secret
kubectl describe secret mydata
이렇게 데이터 크기만 보여줌
다만
kubectl get secret mydata -o yaml
### 하면 보이긴함 근데 인코딩은 암호화된게 아니기때문에 주의해야함
Pod를 생성할 때 Secret은 이걸 설정해주면됨
kubectl explain pod.spec.volumes.secret
### secret name이 있다
키는 파일명 value는 ..
다음은 nginx로 https를 하는 방법을 해볼 예정임
아파치는 너무 무겁고 설정할게 너무 많아서 nginx가 점점 많아지고 있는 추세라고 하심
cat conf/nginx-tls.conf
server {
listen 80;
listen 443 ssl;
server_name myapp.example.com;
ssl_certificate /etc/nginx/ssl/tls.crt;
ssl_certificate_key /etc/nginx/ssl/tls.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
kubectl run nginx --image nginx
kubectl exec -it nginx -- bash
cd /usr/share/nginx/html/
ls
cd ~
cd /etc/nginx
ls
cd conf.d/
ls #여기에 추가적인 설정파일들이 들어갈 에정임
#파일명은 상관이 없고 conf로 끝나면 된다.
kubectl delete ... #아까 만든 Pod삭제
이렇게 https가 되는 것임
이걸 config로해서 어디에 제공해야하는지 고민해보아라
미니 과제
ls cd x509
이런것도 있지만 중요한 것은 Domain이 있어야 사용 가능하다.
지금은 간단하게 테스트하기위해
self signed certificate
ssc
를 사용할 예정
openssl genrsa -out nginx-tls.key 2048 #private키를 만들어준다. out 파일명 지정 키길이를 지정해줄 수 있음
ls
openssl rsa -in nginx-tls.key -pubout -out nginx-tls # 퍼블릭 키를 만들어줌
ls
openssl req -new -key nginx-tls.key -out nginx-tls.csr #Certificate Signing Request를 의미함 서명 요청 인증서
### 이 파일을 Letsencript같은 곳으로 보낼 수 있다.
### 다만 이 무료는 3개월마다 바꾸어주어야
### gabia에서 ssl호스팅을 가보면
ls
### nginx-tls.csr 파일을 만들어줌
openssl req -x509 -days 3650 -key nginx-tls.key -in nginx-tls.csr -out nginx-tls.crt
ls
실제로는 서명된 파일을 줌
필요 없는 파일은 지워줌
이렇게 2개만 있으면 됨
이제 중요한 것은
인증서는 secret으로 등록
이것을 이용할것임
이런 구성이 기본 구성임
Pod를 만들것임
서비스 포트포워딩 등등..
curl 했을 때
curl https://192.168.100.240
자체서명 인증서는 오류가 나기 떄문에
이걸로 확인해야함
curl -k https://x.x.x.x
했을 때 컨텐츠가 나와야함
configmap과
secret을 파일로 제공해서
해결을 해보시오..
성공창..
CA가 없어서 올바르지 않은것임
이렇게말고
이렇게해서 한 줄로 넣기
kubectl get cm
kubectl describe cm nginx-tls-config
kubectl get secret
kubectl describe secret nginx-tls-secret
kubectl get po
kubectl get po describe 로 확인...
kubectl svc,ep,po,cm,secret
다음시간에는
Ingress + Secret = TLS Termination
을 해볼 것이고
StatefullSet을 해볼것임
이를 위해 Headless SVC와 Dynamic Provisioning을 사용할 것임
그리고 Scheduling
Horizontal Pod Autoscaler
RBAC
Helm
Package
Elastic Stack(Log)
Prometeus(Monitoring)
EKS
이렇게하면 쿠버네티스는 어느정도 끝이 남..