diff --git a/README.md b/README.md
new file mode 100644
index 0000000..60b1528
--- /dev/null
+++ b/README.md
@@ -0,0 +1,97 @@
+# 본 과정에 대해 ( Cloud Native 입문 과정 )
+
+본 교육 과정은 Cloud Native 입문 과정으로 이론 및 실습을 수행한다.
+
+
+문의 : 이석환 ( seokhwan.lee@kt.com / shclub@gmail.com )
+
+
+
+1. Chapter 1 : 2시간 ( [가이드 문서보기](./chapter1.md) )
+
+ - Docker 이해 및 활용
+ - GitHub , Docker 계정 생성 , Jenkins Pipeline 생성하여 CI 실습
+ - Swagger 설명
+ - 샘플 소스 : [ 소스 보기 ](https://github.com/shclub/edu2)
+
+
+
+2. Chapter 2 : 2시간 ( [가이드 문서보기](./chapter2.md) )
+
+ - Github Action , workflow 사용 ( GoodBye Jenkins )
+ - Docker Compose 설치 및 활용 ( DB 연동 )
+ - 샘플 소스 : [ 소스 보기 ](https://github.com/shclub/edu1)
+
+
+
+
+3. Chapter 3 : 2시간 ( [가이드 문서보기](./chapter3.md) )
+
+ - k8s 이해 및 활용
+ - k8s hands-on Basic [ Hands-On 문서보기 ](./k8s_basic_hands_on.md)
+
+ - 실습 전체 개요
+ - kubeconfig 설정 : kubectl 설치
+ - kubectl 활용
+ - kubernetes 리소스 ( Pod , Service , Deployment 생성 및 삭제)
+ - 배포 ( Rolling Update / Rollback )
+ - Serivce Expose ( Ingress / Route )
+
+
+
+4. Chapter 4 : 2시간 ( [가이드 문서보기](./chapter4.md) )
+
+ - GitOps 설명
+ - ArgoCD 설치 및 설정
+ - kustomize 설명 및 실습
+ - k8s에 배포 실습 ( Blue/Green , Canary )
+ - ArgoCD Hands-on [ Hands-On 문서보기 ](./argocd_hands_on.md)
+
+ - kubectl plugin 설치
+ - Blue/Green 배포
+ - Canary 배포
+ - ArgoCD 계정 추가 및 권한 할당
+ - kustomize 사용법
+ - ArgoCD remote Cluster 에서 배포 하기
+
+
+
+
+
+## Jenkins 접속 정보
+
+
+
+```bash
+http://211.252.85.148:9000/
+```
+
+
+
+## OKD 접속 정보
+
+
+
+```bash
+oc login https://api.211-34-231-81.nip.io:6443 -u shclub-admin -p New1234! --insecure-skip-tls-verify
+```
+
+
+
+## Node Port용 접속 정보
+
+
+
+```bash
+211.34.231.84
+```
+
+
+
+## ArgoCD 접속 정보
+
+
+
+```bash
+https://211.252.87.34:30000/
+```
diff --git a/argocd_hands_on.md b/argocd_hands_on.md
new file mode 100644
index 0000000..3593545
--- /dev/null
+++ b/argocd_hands_on.md
@@ -0,0 +1,1141 @@
+# ArgoCD Hands-on
+
+ArgoCD 활용 방법에 대해서 실습한다.
+
+1. kubectl plugin 설치
+
+2. Blue/Green 배포
+
+3. Canary 배포
+
+4. ArgoCD 계정 추가 및 권한 관리
+
+5. kustomize 사용법
+
+6. ArgoCD remote Cluster 에서 배포 하기
+
+7. 참고 사이트
+ - https://potato-yong.tistory.com/138
+ - https://teichae.tistory.com/entry/Argo-CD%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-BlueGreen-%EB%B0%B0%ED%8F%AC-3
+ - canary : https://teichae.tistory.com/entry/Argo-CD%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-Canary-%EB%B0%B0%ED%8F%AC-4
+
+
+
+## 실습 전체 개요
+
+
+
+
+### kubectl plugin 설치
+
+
+
+Argo Rollout 기능을 사용하기 위해서 kubectl plugins을 설치한다.
+
+- Mac
+ ```bash
+ brew install argoproj/tap/kubectl-argo-rollouts
+ ```
+
+- linux ( ubuntu )
+ ```bash
+ root@jakelee:~# curl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64
+ % Total % Received % Xferd Average Speed Time Time Time Current
+ Dload Upload Total Spent Left Speed
+ 100 166 100 166 0 0 5187 0 --:--:-- --:--:-- --:--:-- 5187
+ 100 672 100 672 0 0 17684 0 --:--:-- --:--:-- --:--:-- 17684
+ 100 76.7M 100 76.7M 0 0 156M 0 --:--:-- --:--:-- --:--:-- 399M
+ ```
+
+ chmod로 권한을 부여하고 /usr/local/bin 폴더로 이동한다.
+
+ ```bash
+ root@jakelee:~# chmod +x ./kubectl-argo-rollouts-linux-amd64
+ root@jakelee:~# sudo mv ./kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts
+ ```
+
+설치가 완료되고 나서 버전을 확인 한다.
+
+```bash
+root@jakelee:~# kubectl argo rollouts version
+kubectl-argo-rollouts: v1.2.0+08cf10e
+ BuildDate: 2022-03-22T00:25:11Z
+ GitCommit: 08cf10e554fe99c24c8a37ad07fadd9318e4c8a1
+ GitTreeState: clean
+ GoVersion: go1.17.6
+ Compiler: gc
+ Platform: linux/amd64
+```
+
+
+
+### Blue/Green 배포
+
+
+
+Blue/Green 배포란?
+
+참고 : https://youtu.be/qLlo7MAJvT0
+
+
+
+Blue-Green 배포는 애플리케이션 또는 마이크로서비스의 이전 버전에 있던 사용자 트래픽을 이전 버전과 거의 동일한 새 버전으로 점진적으로 이전하는 애플리케이션 릴리스 모델입니다. 이때 두 버전 모두 프로덕션 환경에서 실행 상태를 유지합니다.
+
+이전 버전을 blue 환경으로, 새 버전은 green 환경으로 부를 수 있습니다. 프로덕션 트래픽이 blue에서 green으로 완전히 이전되면, blue는 롤백에 대비하여 대기 상태로 두거나 프로덕션에서 가져온 후 업데이트하여 다음 업데이트의 템플릿으로 삼을 수 있습니다.
+
+이와 같은 지속적 배포 모델에는 단점이 있습니다. 환경에 따라서는 업타임 요구 사항이 다르거나 blue-green과 같은 CI/CD 프로세스를 제대로 수행할 리소스가 없을 수도 있습니다.
+그러나 애플리케이션을 지원하는 기업의 디지털 트랜스포메이션이 본격화되면서 많은 애플리케이션이 이러한 지속적 제공을 지원하도록 진화하고 있습니다.
+
+
+
+기존의 Kubernetes에서도 Deployment 2개를 생성하고 Service의 Selector를 변경해주는 방법으로 Blue/Green 방식의 배포를 할 수 있다.
+하지만 이러한 방법은 Deployment 2개를 운영해야 하기 때문에 번거롭기도 하고 ArgoCD를 사용하면 더 편리하게 Blue/Green 방식으로 배포할 수 있다.
+
+
+
+기존 Kubernetes에서는 1개의 Pod가 각각 Rolling Update 방식으로 배포된다.
+
+1. Blue (2) - Green (0)
+2. Blue (1) - Green (1) <- 이 단계에서 이전 버전과 새로운 버전이 공존하는 현상이 나타남
+3. Blue (0) - Green (2)
+
+이러한 방식을 Blue/Green 방식으로 배포하게 되면,
+1. Blue (2) - Green (0)
+2. Blue (2) - Green (2) <- 이 단계에서 총 4개의 파드가 생성되면서 Green 으로 옮겨간다
+3. Blue (0) - Green (2)
+
+
+아래 예제를 사용하여 Blue/Green 배포할 Rollout과 Service를 생성합니다.
+
+https://github.com/shclub/edu5/blob/master/rollout/blue_green_test.yaml
+
+예제로 보는 Rollout의 yaml파일을 보면 Deployment와 거의 흡사하다.
+여기서 살펴봐야 할 부분은 strategy 옵션과 새로 생성한 2개의 서비스들이다.
+
+Rollout은 2개의 서비스를 이용해 preview와 active로 나누어서 Blue와 Green을 구분하며,
+active 에서는 blue가 보이고, preview에서는 green이 보이게 지정한다.
+
+autoPromotionEnabled 옵션은 자동으로 배포할 것인지, 관리자가 수동으로 승인할 것인지에 대한 여부를 묻는 옵션이다.
+
+본 가이드에서는 kubectl plugin을 설치했으니, 승인 과정까지 보여드리기 위해 autoPromotionEnabled: false로 진행했습니다.
+
+Blue/Green을 배포할 예제 yaml 파일이 준비가 되었다면, 이제 Argo CD에서 배포를 해보자.
+
+blue_green_test 라는 이름으로 yaml 파일이 저장된 레포지토리를 지정해서 새로운 애플리케이션을 배포해주자.
+
+
+```bash
+# ArgoCD Blue/Green 배포 예제
+
+apiVersion: argoproj.io/v1alpha1 # apps/v1 대신 argoproj.io/v1alpha1을 사용한다
+kind: Rollout # Deployment 대신 Rollout을 사용한다
+metadata:
+ name: rollout-bluegreen
+spec:
+ replicas: 2
+ revisionHistoryLimit: 2
+ selector:
+ matchLabels:
+ app: rollout-bluegreen
+ template:
+ metadata:
+ labels:
+ app: rollout-bluegreen
+ spec:
+ containers:
+ - name: rollouts-demo
+ image: argoproj/rollouts-demo:blue
+ #image: argoproj/rollouts-demo:green
+ imagePullPolicy: Always
+ ports:
+ - containerPort: 8080
+ strategy:
+ blueGreen:
+ #activeService는 이전의 배포된 Blue 서비스
+ activeService: rollout-bluegreen-active
+
+ #previewService는 새롭게 배포될 Green 서비스
+ previewService: rollout-bluegreen-preview
+
+ #autoPromotioEnabled 옵션은 Blue/Green 배포를 자동으로 진행할 것인지 여부. false 옵션을 사용해 수동으로 지정
+ autoPromotionEnabled: false
+```
+
+ArgoCD에서 New App를 클릭하고 아래와 같이 설정하고 Create 한다.
+namespace 는 rollout-demo로 자동 생성되게 설정한다.
+
+
+
+
+배포가 정상적으로 되었는지 확인한다.
+
+
+
+아래 명령어를 사용하여 서비스의 NodePort를 확인한다.
+
+```bash
+root@jakelee:~# kubectl get all -n rollout-demo
+NAME READY STATUS RESTARTS AGE
+pod/rollout-bluegreen-5ffd47b8d4-zlkfl 1/1 Running 0 16s
+pod/rollout-bluegreen-5ffd47b8d4-rttxs 1/1 Running 0 16s
+
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+service/rollout-bluegreen-preview NodePort 10.43.4.240 80:30082/TCP 16s
+service/rollout-bluegreen-active NodePort 10.43.213.203 80:30081/TCP 16s
+
+NAME DESIRED CURRENT READY AGE
+replicaset.apps/rollout-bluegreen-5ffd47b8d4 2 2 2 16s
+```
+
+Blue Green이 배포되었으니 확인하기 위해서 active와 preview 서비스의 노드 포트를 통해서 접속해보자.
+
+- active 접속 : ( 서버 IP ) :30081
+- preview 접속 : ( 서버 IP ) :30082
+
+배포를 진행하고 active 서비스로 접속을 해보면 Blue 페이지가 표시되는 것을 확인할 수 있다.
+
+현재는 blue만 배포되어 있기 때문에 active와 preview 둘다 blue만 보일것이다.
+
+
+
+다음으로 이전에 배포했던 yaml파일에서 주석처리 되어있던 Green 이미지를 사용하여 다시 재배포해주자.
+
+
+
+그리고 실행되고 있는 pod를 확인해보면 총 4개의 pod가 생성되어 있고, 먼저 생성된 것이 blue, 나중에 새로 생겨난것이 green이다.
+
+여기까지 진행하면 active에는 blue가 배포 되어있고, preview에 green이 배포되어 있는것을 직접 확인할 수 있다.
+
+
+
+```bash
+root@jakelee:~# kubectl get po -n rollout-demo
+NAME READY STATUS RESTARTS AGE
+rollout-bluegreen-5ffd47b8d4-zlkfl 1/1 Running 0 11m
+rollout-bluegreen-5ffd47b8d4-rttxs 1/1 Running 0 11m
+rollout-bluegreen-75695867f-mnblz 1/1 Running 0 50s
+rollout-bluegreen-75695867f-rxw9k 1/1 Running 0 50s
+```
+
+preview에 접속해보면 green 변경 된것을 확인 할수 있다. active는 현재 blue 이다.
+
+
+
+
+
+Blue/Green 교체 승인
+
+Blue/Green yaml 파일에서 autoPromotionEnabled 옵션을 false로 주었기 때문에 Blue에서 Green으로 자동으로 배포되지 않고 정지되어 있는 상태를 확인할 수 있다.
+
+이후, 정상적으로 Blue/Green이 배포된것을 확인했다면 Green으로 교체해주는 과정을 진행해주어야 한다.
+
+- argocd ui에서 진행하는 방법
+
+ rollout 화면에서 파란색 아이콘으로 paused 된 상태를 볼수 있고 오른쪽 점 표시를 클릭하면 여러가지 메뉴를 볼수 있는데 승인을 하기 위해서는 resume을 클릭한다.
+
+
+
+ 실행을 하기 위해서 OK를 클릭한다.
+
+
+
+- 명령 모드 에서 진행하는 방법
+ ```bash
+ #rollout 상태 확인 : STATUS는 Paused
+ root@jakelee:~# kubectl argo rollouts list rollout -n rollout-demo
+ NAME STRATEGY STATUS STEP SET-WEIGHT READY DESIRED UP-TO-DATE AVAILABLE
+ rollout-bluegreen BlueGreen Paused - - 2/4 2 2 2
+ ```
+
+ rollout 상태 확인 후 승인
+
+ ```bash
+ #rollout 상태 확인 후 승인
+ root@jakelee:~# kubectl argo rollouts promote rollout-bluegreen -n rollout-demo
+ rollout 'rollout-bluegreen' promoted
+ ```
+
+rollout 상태 확인
+
+```bash
+#rollout 상태 확인 (healthy)
+root@jakelee:~# kubectl argo rollouts list rollout -n rollout-demo
+NAME STRATEGY STATUS STEP SET-WEIGHT READY DESIRED UP-TO-DATE AVAILABLE
+rollout-bluegreen BlueGreen Healthy - - 2/4 2 2 2
+```
+
+promote 과정을 진행하면 rollouts의 상태가 healthy로 변하고 blue 배포되었던 pod가 종료된다.
+
+```bash
+#pod 상태 확인 (healthy)
+root@jakelee:~# kubectl get po -n rollout-demo
+NAME READY STATUS RESTARTS AGE
+rollout-bluegreen-75695867f-mnblz 1/1 Running 0 10m
+rollout-bluegreen-75695867f-rxw9k 1/1 Running 0 10m
+rollout-bluegreen-5ffd47b8d4-zlkfl 1/1 Terminating 0 20m
+rollout-bluegreen-5ffd47b8d4-rttxs 1/1 Terminating 0 20m
+root@jakelee:~# kubectl get po -n rollout-demo
+NAME READY STATUS RESTARTS AGE
+rollout-bluegreen-75695867f-mnblz 1/1 Running 0 13m
+rollout-bluegreen-75695867f-rxw9k 1/1 Running 0 13m
+```
+
+이때 다시 active와 preview 로 접속해보면 모두 Green으로 표시되고 무사히 Blue에서 Green으로 교체되는 모습을 볼 수 있다.
+
+
+
+
+
+### Canary 배포
+
+
+
+Canary 배포란?
+
+Canary 배포는 기존에 배포된 서비스에 신규 서비스를 한꺼번에 배포/교체를 진행하지 않고 소량의 Pod만 일시적으로 배포하는 방식입니다.
+
+Canary 방식의 어원을 살펴보자면, `카나리아`라는 새에서 나오게 되었다.
+
+
+`카나리아는 메탄, 일산화탄소에 매우 민감한 새라 가스에 노출되면 죽어버리게 된다. 그래서 옛날에 광부들이 안전하게 일 할 수 있도록 카나리아를 이용하였다. 카나리아가 노래를 계속하고 있는 동안 광부들은 안전함을 느낀 채 일 할 수 있었으며, 만약 카나리아가 죽게 되면 곧바로 탄광을 탈출함으로써 광부들의 생명을 보존할 수 있었다.`
+
+Canary 방식은 카나리아 새처럼 위험을 빠르게 감지할 수 있는 방식이다.
+
+특정 서버나 소수의 유저들만 먼저 새로운 버전을 배포하고 사용하고 나서 안전하다고 판단이 되면 그 후에 모든 서버들에 새로운 버전을 배포하는 방식이다.
+
+
+
+배포를 위한 소스는 아래 와 같습니다. nodeport는 blue/green 예제와 충돌 나지 않도록 30083을 사용합니다.
+
+- 소스 : https://github.com/shclub/edu5/blob/master/canary/canary.yaml
+
+```bash
+apiVersion: argoproj.io/v1alpha1
+kind: Rollout
+metadata:
+ name: canary-rollout
+spec:
+ replicas: 8
+ revisionHistoryLimit: 2
+ selector:
+ matchLabels:
+ app: canary
+ template:
+ metadata:
+ labels:
+ app: canary
+ spec:
+ containers:
+ - name: canary-rollouts-demo
+ image: particule/simplecolorapi:1.0
+ imagePullPolicy: Always
+ ports:
+ - containerPort: 5000
+ strategy:
+ canary:
+ maxSurge: "25%"
+ maxUnavailable: 0
+ steps:
+ - setWeight: 25
+ - pause: {}
+---
+kind: Service
+apiVersion: v1
+metadata:
+ name: canary-service
+spec:
+ selector:
+ app: canary
+ ports:
+ - protocol: TCP
+ port: 80
+ targetPort: 5000
+ nodePort: 30083
+ type: NodePort
+```
+
+아래는 옵션 설명입니다.
+
+```bash
+ strategy:
+ canary:
+ maxSurge: "25%"
+ maxUnavailable: 0
+ steps:
+ - setWeight: 25
+ - pause: {}
+```
+
+maxSurge는 배포되는 Pod의 비율을 뜻하고, maxUnavailable는 배포될 때 Unavailable되도 되는 Pod의 수를 뜻합니다.
+
+steps에서 setWeight는 Weight 값을 주어 트래픽을 어느 정도 인가하는지에 대한 옵션입니다.
+
+pause는 Blue/Green 처럼 AutoPromotion Time을 뜻합니다.
+
+아래와 같이 시간을 지정할 수 있습니다.
+
+```bash
+ - pause: { duration: 10 } # 10초
+ - pause: { duration: 10s } # 10초
+ - pause: { duration: 10m } # 10분
+ - pause: { duration: 10h } # 10시간
+ - pause: { duration: -10 } # 잘못된 옵션
+ - pause: {} # Auto Promotion 옵션 비활성화
+```
+
+New App으로 새로운 배포 구성을 합니다. 설정은 Blue/Green을 참고하고 path는 ./canary로 설정한다.
+
+
+
+
+실행을 하면 pod가 8개가 생성된것을 확인 할 수 있다.
+
+
+
+배포가 완료되었습니다. Canary page에 접속해보겠습니다.
+Red라는 응답값이 나오게 됩니다.
+
+
+
+이제 2.0 버전의 이미지를 배포하여, 25%의 배포 및 트래픽 인가를 해보겠습니다.
+
+코드는 아래처럼 배포할 이미지 태그를 수정하면 됩니다.
+
+
+
+배포 후 신규 Replicaset이 생성되며 8개의 Pod의 25%니까 2개가 새로 배포된 것을 확인할 수 있습니다.
+
+
+
+원활한 테스트를 진행하기 위해 커맨드라인 JSON 파서인 jq 라이브러리를 설치합니다.
+
+```bash
+root@jakelee:~# apt install jq
+Reading package lists... Done
+Building dependency tree
+Reading state information... Done
+The following additional packages will be installed:
+ libjq1 libonig4
+The following NEW packages will be installed:
+ jq libjq1 libonig4
+0 upgraded, 3 newly installed, 0 to remove and 4 not upgraded.
+Need to get 276 kB of archives.
+After this operation, 930 kB of additional disk space will be used.
+Do you want to continue? [Y/n] Y
+Get:1 http://kr.archive.ubuntu.com/ubuntu bionic/universe amd64 libonig4 amd64 6.7.0-1 [119 kB]
+Get:2 http://kr.archive.ubuntu.com/ubuntu bionic/universe amd64 libjq1 amd64 1.5+dfsg-2 [111 kB]
+Get:3 http://kr.archive.ubuntu.com/ubuntu bionic/universe amd64 jq amd64 1.5+dfsg-2 [45.6 kB]
+Fetched 276 kB in 2s (117 kB/s)
+Selecting previously unselected package libonig4:amd64.
+(Reading database ... 147313 files and directories currently installed.)
+Preparing to unpack .../libonig4_6.7.0-1_amd64.deb ...
+Unpacking libonig4:amd64 (6.7.0-1) ...
+Selecting previously unselected package libjq1:amd64.
+Preparing to unpack .../libjq1_1.5+dfsg-2_amd64.deb ...
+Unpacking libjq1:amd64 (1.5+dfsg-2) ...
+Selecting previously unselected package jq.
+Preparing to unpack .../jq_1.5+dfsg-2_amd64.deb ...
+Unpacking jq (1.5+dfsg-2) ...
+Setting up libonig4:amd64 (6.7.0-1) ...
+Setting up libjq1:amd64 (1.5+dfsg-2) ...
+Setting up jq (1.5+dfsg-2) ...
+Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
+Processing triggers for libc-bin (2.27-3ubuntu1.5) ...
+```
+
+0.5초마다 curl을 실행하여 테스트할 때, 정상적으로 Canary 배포가 되어 있는 점을 확인할 수 있습니다.
+
+아래 구문에서 ip는 본인 VM Public IP를 사용합니다.
+
+```bash
+root@jakelee:~# while true; do curl http://210.106.105.165:30083 | jq .color; sleep 0.5; done
+```
+
+
+
+
+배포가 정상적으로 이루어졌고, canary 버전이 문제가 없기 때문에 기존에 배포된 내용을 걷어내고, canary 버전을 완전히 배포해보겠습니다.
+
+```bash
+root@jakelee:~# kubectl argo rollouts list rollout -n canary
+NAME STRATEGY STATUS STEP SET-WEIGHT READY DESIRED UP-TO-DATE AVAILABLE
+canary-rollout Canary Paused 1/2 25 8/8 8 2 8
+```
+
+Blue/Green 때처럼 Status가 Pause 상태인 것을 확인할 수 있습니다.
+
+Promote명령어를 이용하여 배포를 진행합니다.
+
+```bash
+root@jakelee:~# kubectl argo rollouts promote canary-rollout -n canary
+rollout 'canary-rollout' promoted
+root@jakelee:~# kubectl argo rollouts list rollout -n canary
+NAME STRATEGY STATUS STEP SET-WEIGHT READY DESIRED UP-TO-DATE AVAILABLE
+canary-rollout Canary Progressing 2/2 100 8/10 8 4 8
+root@jakelee:~# kubectl argo rollouts list rollout -n canary
+NAME STRATEGY STATUS STEP SET-WEIGHT READY DESIRED UP-TO-DATE AVAILABLE
+canary-rollout Canary Progressing 2/2 100 8/10 8 6 8
+root@jakelee:~# kubectl argo rollouts list rollout -n canary
+NAME STRATEGY STATUS STEP SET-WEIGHT READY DESIRED UP-TO-DATE AVAILABLE
+canary-rollout Canary Progressing 2/2 100 8/10 8 8 8
+root@jakelee:~# kubectl argo rollouts list rollout -n canary
+NAME STRATEGY STATUS STEP SET-WEIGHT READY DESIRED UP-TO-DATE AVAILABLE
+canary-rollout Canary Healthy 2/2 100 8/8 8 8 8
+```
+
+배포가 완료되고 Status가 Healthy로 변경된 것을 확인할 수 있습니다.
+
+
+
+다시 한번 0.5초마다 curl을 실행하여 하여 보면 모두 blue로 변경된걸 확인 할 수 있다.
+
+```bash
+root@jakelee:~# while true; do curl http://210.106.105.165:30083 | jq .color; sleep 0.5; done
+```
+
+
+
+
+
+
+
+### ArgoCD 계정 추가
+
+
+
+ArgoCD 신규 계정을 생성한다. GUI에서는 불가능하고 argocd cli를 사용한다.
+
+argocd-server의 서비스 IP를 확인한다.
+
+```bash
+oot@jakelee:~# kubectl get svc -n argocd
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+argocd-applicationset-controller ClusterIP 10.43.26.65 7000/TCP 12d
+argocd-dex-server ClusterIP 10.43.239.221 5556/TCP,5557/TCP,5558/TCP 12d
+argocd-metrics ClusterIP 10.43.251.44 8082/TCP 12d
+argocd-notifications-controller-metrics ClusterIP 10.43.214.197 9001/TCP 12d
+argocd-redis ClusterIP 10.43.12.131 6379/TCP 12d
+argocd-repo-server ClusterIP 10.43.132.197 8081/TCP,8084/TCP 12d
+argocd-server-metrics ClusterIP 10.43.200.82 8083/TCP 12d
+argocd-server NodePort 10.43.247.167 80:30000/TCP,443:30001/TCP 12d
+inspekt ClusterIP 10.43.156.159 80/TCP 3d17h
+```
+
+cluster ip로 로그인 한다.
+
+```bash
+root@jakelee:~# argocd login 10.43.247.167
+WARNING: server is not configured with TLS. Proceed (y/n)? y
+Username: admin
+Password:
+'admin:login' logged in successfully
+Context '10.43.247.167' updated
+```
+
+새로운 account 는 shclub 입니다.
+
+ArgoCD 의 account 추가는 ArgoCD의 Configmap을 통해서 가능합니다.
+
+아래 명령을 실행 합니다.
+
+
+```bash
+root@jakelee:~# kubectl -n argocd edit configmap argocd-cm -o yaml
+apiVersion: v1
+data:
+ accounts.shclub: apiKey,login
+kind: ConfigMap
+metadata:
+ annotations:
+ kubectl.kubernetes.io/last-applied-configuration: |
+ {"apiVersion":"v1","kind":"ConfigMap","metadata":{"annotations":{},"labels":{"app.kubernetes.io/name":"argocd-cm","app.kubernetes.io/part-of":"argocd"},"name":"argocd-cm","namespace":"argocd"}}
+ creationTimestamp: "2022-04-01T08:31:45Z"
+ labels:
+ app.kubernetes.io/name: argocd-cm
+ app.kubernetes.io/part-of: argocd
+ name: argocd-cm
+ namespace: argocd
+ resourceVersion: "903340"
+ uid: 1ea9382d-052b-43e9-b1b4-6d212efee1ec
+```
+계정을 생성하기 위해 2개 라인을 추가합니다.
+아래에서 shclub는 추가할 계정이름이다. 본인의 계정으로 설정.
+
+```bash
+data:
+ accounts.shclub: apiKey,login
+```
+
+계정 리스트를 통해 신규 계정 생성을 확인합니다.
+
+```bash
+root@jakelee:~# argocd account list
+NAME ENABLED CAPABILITIES
+admin true login
+shclub true apiKey, login
+root@jakelee:~# argocd account get --account shclub
+Name: shclub
+Enabled: true
+Capabilities: apiKey, login
+
+Tokens:
+NONE
+```
+
+비밀번호를 변경합니다. 8자리 이상으로 설정.
+
+```bash
+root@jakelee:~# argocd account update-password --account shclub
+*** Enter password of currently logged in user (admin):
+*** Enter new password for user shclub:
+*** Confirm new password for user shclub:
+Password updated
+```
+
+해당 계정으로 로그인 하면 admin 계정으로 생성한 배포 pipeline을 볼 수 없습니다.
+
+
+
+
+ArgoCD가 사용하는 RBAC 규칙에 맞게 새롭게 permission 을 할당해주어야 합니다.
+
+ArgoCD RBAC 을 추가하려면 ArgoCD Confimap 인 argocd-rbac-cm 을 수정해야 합니다. 다음 명령을 실행 하여 수정을 시작 합니다.
+
+shclub는 신규 생성한 계정이고 여러분의 계정으로 변경하여 저장하시면 됩니다.
+
+```bash
+root@jakelee:~# kubectl -n argocd edit configmap argocd-rbac-cm -o yaml
+apiVersion: v1
+data:
+ policy.csv: |
+ p, role:manager, applications, *, */*, allow
+ p, role:manager, clusters, get, *, allow
+ p, role:manager, repositories, *, *, allow
+ p, role:manager, projects, *, *, allow
+ g, shclub, role:manager
+ policy.default: role:readonly
+kind: ConfigMap
+metadata:
+ annotations:
+ kubectl.kubernetes.io/last-applied-configuration: |
+ {"apiVersion":"v1","kind":"ConfigMap","metadata":{"annotations":{},"labels":{"app.kubernetes.io/name":"argocd-rbac-cm","app.kubernetes.io/part-of":"argocd"},"name":"argocd-rbac-cm","namespace":"argocd"}}
+ creationTimestamp: "2022-04-01T08:31:45Z"
+ labels:
+ app.kubernetes.io/name: argocd-rbac-cm
+ app.kubernetes.io/part-of: argocd
+ name: argocd-rbac-cm
+ namespace: argocd
+ resourceVersion: "904712"
+ uid: 5493d81d-74a4-4f55-830b-919a924d7440
+```
+
+
+
+role은 manager 라는 이름으로 생성하였고 거의 full 권한을 가지고 있습니다.
+
+향후에 좀더 detail 하게 설정 할 수 있습니다.
+
+```bash
+data:
+ policy.csv: |
+ p, role:manager, applications, *, */*, allow
+ p, role:manager, clusters, get, *, allow
+ p, role:manager, repositories, *, *, allow
+ p, role:manager, projects, *, *, allow
+ g, shclub, role:manager
+ policy.default: role:readonly
+```
+
+ArgoCD 화면에 admin 계정으로 생성한 pipeline 을 볼수 있습니다.
+
+
+
+
+
+운영 환경에서는 권한을 최소화 하여야 합니다.
+
+먼저 default 라고 하는 project 와는 별도로 신규 project를 생성합니다.
+
+아래 내용을 복사하고 vi 에디터로 argocd_proj.yaml 화일을 만들고 붙여 넣기 한 후 저장한다.
+
+
+
+```yaml
+apiVersion: argoproj.io/v1alpha1
+kind: AppProject
+metadata:
+ name: edu-project
+ namespace: argocd
+spec:
+ clusterResourceWhitelist:
+ - group: '*'
+ kind: '*'
+ destinations:
+ - namespace: '*'
+ server: '*'
+ orphanedResources:
+ warn: false
+ sourceRepos:
+ - '*'
+```
+
+
+
+아래 명령어를 사용하여 project를 생성한다.
+
+```bash
+root@jakelee:~# kubectl apply -f argocd_proj.yaml
+```
+
+에러가 없이 발생하면 edu-project 라는 이름으로 project 가 생성된 것을 확인 할 수 있다.
+
+```bash
+root@jakelee:~# kubectl get appproject -n argocd
+NAME AGE
+default 53d
+edu-project 88m
+```
+
+
+
+아래 처럼 default role을 readonly에서 모든 기능을 disable 하도록 한다.
+
+기존
+```bash
+ policy.default: role:readonly
+```
+
+변경
+```bash
+ policy.default: role:''
+```
+
+
+
+권한을 세부 적으로 컨트롤 한다.
+
+edu1 이라는 신규 생성은 edu-project라고 하는 project에 한하여만 전체 권한을 갖는다.
+
+edu-project 이외의 applications 들은 볼수 없습니다.
+
+```bash
+ p, role:edu1, clusters, get, *, allow
+ p, role:edu1, repositories, get, *, allow
+ p, role:edu1, projects, get, *, allow
+ p, role:edu1, applications, *, edu-project/*, allow
+```
+
+
+
+아래 처럼 프로젝트 명을 적어 주면 해당 프로젝트만 보입니다.
+
+
+```bash
+ p, role:edu1, projects, get, edu-project, allow
+```
+
+
+
+전체 내용은 아래와 같다.
+
+
+```bash
+data:
+ policy.csv: |
+ p, role:manager, applications, *, */*, allow
+ p, role:manager, clusters, get, *, allow
+ p, role:manager, repositories, *, *, allow
+ p, role:manager, projects, *, *, allow
+ p, role:edu1, clusters, get, *, allow
+ p, role:edu1, repositories, get, *, allow
+ p, role:edu1, projects, get, *, allow
+ p, role:edu1, applications, *, edu-project/*, allow
+ g, edu1, role:edu1
+ g, shclub, role:manager
+ policy.default: role:''
+```
+
+
+
+### kustomize
+
+
+
+kustomize란?
+- https://kubernetes.io/ko/docs/tasks/manage-kubernetes-objects/kustomization/
+
+
+
+kubernetes의 배포 도구 중 하나로, kubectl v1.14에 통합되었음
+
+기존에 helm chart를 통해 application 배포를 수행하였으나, helm value를 추가 하기 위해서는 helm template chart의 수정이 필요했음.
+
+kustomize는 기본적으로 사용되던 yaml을 그대로 사용하며, kustomization.yaml과 base, overay 및 production, development등 디렉토리를 환경에 맞게 구성하여 별도의 데이터 기반으로 배포 관리를 할수 있음.
+
+overlay는 패치가 필요한 쿠버네티스 리소스에는 변경하고자 하는 부분만 YAML로 만들어서 패치 형태로 쓸 수 있다.
+
+
+
+
+
+base
+
+- 기본 yaml file들이 저장되는 경로
+ - 해당폴더에 kustomization.yaml 파일이 필히 존재해야 함
+- https://github.com/kubernetes-sigs/kustomize#1-make-a-kustomization-file
+- 기존에 사용되던 yaml파일들을 여기에 복사해두고 kustomization.yaml에 사용할 yaml을 선언해두면 된다.
+
+
+
+
+
+
+overlays
+
+- 일반적으로 base에 사용되는 yaml을 기반으로 patch하여 업데이트 하는 방식으로 사용
+- 하위 디렉토리에서 느낄수 있듯이 stage 별로 나누거나 공용으로 사용되는 base에 특정한 value를 추가해야 하는 경우 사용된다.
+- https://github.com/kubernetes-sigs/kustomize#2-create-variants-using-overlays
+- overlays 하위에 디렉토리로 staging, dev, production 등 다양한 stage별 식별이 가능한 옵션을 두고 관리할수 있다.
+
+
+
+
+
+테스트를 위한 config는 https://github.com/shclub/edu6 를 참고한다.
+
+현재 화일 구조는 아래와 같다.
+
+```
+├── base
+│ ├── service.yaml
+│ ├── deployment.yaml
+│ ├── kustomization.yaml
+└── overlays
+ └── development
+ ├── kustomization.yaml
+ └── cpu_count.yaml
+ └── replica_count.yaml
+ └── production
+ ├── kustomization.yaml
+ └── cpu_count.yaml
+ └── replica_count.yaml
+
+```
+
+base 폴더의 kustomzation.yaml 화일
+
+```
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+- deployment.yaml
+- service.yaml
+```
+
+overlays/development 폴더의 kustomization.yaml 화일
+- namePrefix : pod의 이름 앞에 붙는다.
+
+```
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+images:
+- name: shclub/edu4
+ newTag: v1
+namePrefix: dev-
+bases:
+- ../../base
+patchesStrategicMerge:
+- replica_count.yaml
+- cpu_count.yaml
+```
+
+cpu_count.yaml 화일
+- cpu max 값 설정
+
+```
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: edu6
+spec:
+ template:
+ spec:
+ containers:
+ - name: edu6
+ resources:
+ limits:
+ cpu: 300m
+```
+
+replica_count.yaml 화일
+- replica 설정
+
+```
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: edu6
+spec:
+ replicas: 2
+```
+
+
+
+먼저 kustomize를 설치한다.
+
+아래 명령어 하나로 설치가 된다.
+
+```bash
+root@jakelee:~# curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
+
+{Version:kustomize/v4.5.4 GitCommit:cf3a452ddd6f83945d39d582243b8592ec627ae3 BuildDate:2022-03-28T23:12:45Z GoOs:linux GoArch:amd64}
+kustomize installed to //root/kustomize
+```
+
+-bash: kustomize: command not found 에러가 발생하면
+아래 명령어를 실행한다.
+
+```bash
+mv kustomize /usr/local/bin/
+```
+
+우리는 ArgoCD를 통해서 kustomize를 연동하도록 한다.
+ArgoCD에서 New App을 아래와 같이 만든다.
+
+
+
+
+sync를 해보면 replica 2개로 설정되어 pod가 2개가 생성 된걸 볼수 있다.
+
+
+
+POD의 resource는 300m으로 최대값이 설정되어 있다.
+
+
+
+
+
+참고 : 명령어로 작업하기
+
+
+kustomize 설정 확인.
+github의 shclub/edu6에 설정 되어 있는 화일로 테스트 한다.
+- git clone은 하고 다운받은 화일로 하면 됨.
+
+```bash
+root@jakelee:~# kustomize build https://github.com/shclub/edu6/overlays/development/
+apiVersion: v1
+kind: Service
+metadata:
+ name: dev-edu6
+spec:
+ ports:
+ - port: 80
+ protocol: TCP
+ targetPort: 80
+ selector:
+ run: edu6
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: dev-edu6
+spec:
+ replicas: 2
+ selector:
+ matchLabels:
+ run: edu6
+ template:
+ metadata:
+ labels:
+ run: edu6
+ spec:
+ containers:
+ - image: shclub/edu4:v1
+ name: edu6
+ ports:
+ - containerPort: 80
+ resources:
+ limits:
+ cpu: 300m
+```
+
+kustomize 적용
+
+```bash
+kubectl apply -k https://github.com/shclub/edu6/overlays/development/
+```
+
+
+
+
+### argocd remote 에서 배포 하기
+
+
+
+argocd 를 다른 cluster를 통해서 배포해 본다.
+
+접속하고자 하는 k8s cluster의 config 정보를 복사한다.
+
+.kube 폴더 밑에 config 화일을 하나 추가한다.
+
+vi 에디터로 사용한다.
+
+```
+root@jake-Wyse-mint2:~/.kube# vi config-epc-jakelee
+```
+
+복사한 config 화일 내용을 붙여넣기 한다.
+아래는 샘플 내용이다.
+
+```bash
+apiVersion: v1
+clusters:
+- cluster:
+ certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1J-----SUJkekNDQVIyZ0F3SUJBZ0l
+ server: https://210.106.105.165:6443
+ name: k3s-test
+contexts:
+- context:
+ cluster: k3s-test
+ user: k3s-test
+ name: k3s-test
+current-context: k3s-test
+kind: Config
+preferences: {}
+users:
+- name: k3s-test
+ user:
+ client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FUZ0F3SUJBZ0lJTzJxVnRk-----0tLQo=
+```
+
+새로운 config을 /etc/profile 에 추가한다.
+
+```bash
+root@jake-Wyse-mint2:~/.kube# vi /etc/profile
+```
+
+추가할 내용은 아래와 같다.
+
+```bash
+export KUBECONFIG=/etc/rancher/k3s/k3s.yaml:/root/.kube/config-epc-jakelee:$KUBECONFIG
+```
+
+source 명령어를 사용하여 적용한다.
+
+```bash
+root@jake-Wyse-mint2:~/.kube# source /etc/profile
+```
+
+정상적으로 추가가 된지 확인한다.
+
+```bash
+root@jake-Wyse-mint2:~/.kube# kubectl config get-contexts
+CURRENT NAME CLUSTER AUTHINFO NAMESPACE
+* default default default
+ k3s-test k3s-test k3s-test
+```
+
+k3s-test 클러스터에 접속하기 위해서 context를 switch 해본다.
+
+```bash
+root@jake-Wyse-mint2:~/.kube# kubectl config use-context k3s-test
+Switched to context "k3s-test".
+```
+
+리모트 k8s 접속이 확인 되었으면 다시 로컬 클러스터로 switch 한다.
+
+```bash
+root@jake-Wyse-mint2:~/.kube# kubectl config use-context default
+Switched to context "default".
+```
+
+argo cd 에 cli 로 로그인 하기 위해서 서비스의 ip를 확인한다. ( 로컬 k8s 임 )
+
+```bash
+root@jake-Wyse-mint2:~/.kube# kubectl get svc -n argocd
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+argocd-dex-server ClusterIP 10.43.159.136 5556/TCP,5557/TCP,5558/TCP 69d
+argocd-metrics ClusterIP 10.43.4.89 8082/TCP 69d
+argocd-redis ClusterIP 10.43.124.226 6379/TCP 69d
+argocd-repo-server ClusterIP 10.43.221.146 8081/TCP,8084/TCP 69d
+argocd-server-metrics ClusterIP 10.43.148.232 8083/TCP 69d
+argocd-server NodePort 10.43.188.106 80:32000/TCP,443:30904/TCP 69d
+```
+
+argocd server로 로그인한다.
+insecurely 접속을 물어보면 y를 입력한다.
+
+```bash
+root@jake-Wyse-mint2:~/.kube# argocd login 10.43.188.106
+WARNING: server certificate had error: x509: cannot validate certificate for 10.43.188.106 because it doesn't contain any IP SANs. Proceed insecurely (y/n)? y
+Username: admin
+Password:
+```
+
+cluster 추가 명령어는 아래와 같다.
+
+```bash
+argocd cluster add <클러스터 이름 >
+```
+
+argocd에 외부 k8s cluster를 추가해 본다.
+
+```bash
+root@jake-Wyse-mint2:~/.kube# argocd cluster add k3s-test
+WARNING: This will create a service account `argocd-manager` on the cluster referenced by context `k3s-test` with full cluster level admin privileges. Do you want to continue [y/N]? y
+INFO[0004] ServiceAccount "argocd-manager" created in namespace "kube-system"
+INFO[0004] ClusterRole "argocd-manager-role" created
+INFO[0004] ClusterRoleBinding "argocd-manager-role-binding" created
+Cluster 'https://210.106.105.165:6443' added
+```
+
+
+
+web browser로 argocd ui 를 접속하여 setting-> Cluster 메뉴로 이동하면 cluster가 추가 된 것을 확인한다.
+
+
+
+배포 구성을 할때 remote 클러스터 선택 할 수 있다.
+
+
+
+### 참고
+
+
+
+참고사이트
+- https://tech.kakao.com/2021/07/16/devops-for-msa/
+- https://velog.io/@wlgns5376/GitOps-ArgoCD와-Kustomize를-이용해-kubernetes에-배포하기
+- https://github.com/wlgns5376/example-app-kustomize/
+- https://asuraiv.tistory.com/22?category=877062
+- argocd 권한 : https://intrepidgeeks.com/tutorial/argocd-users-access-and-rbac
+- k8s 전체 개념 :https://www.slideshare.net/gamzabaw/kubernetes-walkthrough
+
+
+
+
+### Helm -> Kustomize 전환
+
+
+
+Replicated의 쉽(Ship) 사용
+
+이제, Helm을 Kustomize 형태로 변환하는 작업이 필요합니다. 이를 도와주는 도구가 쉽(Ship)입니다.
+
+https://www.replicated.com/ship/oss/
+https://github.com/replicatedhq/ship
+
+
+- 먼저 Ship을 사용해 Helm을 템플릿화된 쿠버네티스 리소스 형태로 변경합니다.
+- 이후 쿠버네티스 리소스에서 필요한 부분만 Kustomize 패치를 사용해 변경하고 변경한 부분은 GitOps 리포지토리에 넣어서 관리합니다.
+
+- GitOps 리포지토리에 `git push origin HEAD:install/ ${cluster}/${namespace}/${app_name}` 과 같은 Git CLI 명령을 통해 install 브랜치가 생성이 되면 해당 브랜치의 `${app_name}` 인프라를 배포하는 CI/CD 파이프라인이 실행됩니다.
+- 배포의 형상은 마찬가지로 Argo CD를 통해 GitOps로 관리합니다.
+
+### 과제
+
+
+
+과제 1 : kustomize를 production 에서 argocd로 배포 한다.
+ ( shclub/edu6/overlays/production 참고)
\ No newline at end of file
diff --git a/assets/.DS_Store b/assets/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/assets/.DS_Store differ
diff --git a/assets/apt_update.png b/assets/apt_update.png
new file mode 100644
index 0000000..e8ebf83
Binary files /dev/null and b/assets/apt_update.png differ
diff --git a/assets/argo_rollouts_all.png b/assets/argo_rollouts_all.png
new file mode 100644
index 0000000..df6d134
Binary files /dev/null and b/assets/argo_rollouts_all.png differ
diff --git a/assets/argo_rollouts_install.png b/assets/argo_rollouts_install.png
new file mode 100644
index 0000000..6e9a100
Binary files /dev/null and b/assets/argo_rollouts_install.png differ
diff --git a/assets/argocd_bluegreen1.png b/assets/argocd_bluegreen1.png
new file mode 100644
index 0000000..7b5a0dd
Binary files /dev/null and b/assets/argocd_bluegreen1.png differ
diff --git a/assets/argocd_bluegreen2.png b/assets/argocd_bluegreen2.png
new file mode 100644
index 0000000..e603137
Binary files /dev/null and b/assets/argocd_bluegreen2.png differ
diff --git a/assets/argocd_bluegreen3.png b/assets/argocd_bluegreen3.png
new file mode 100644
index 0000000..db4cecc
Binary files /dev/null and b/assets/argocd_bluegreen3.png differ
diff --git a/assets/argocd_bluegreen4.png b/assets/argocd_bluegreen4.png
new file mode 100644
index 0000000..e3d3d04
Binary files /dev/null and b/assets/argocd_bluegreen4.png differ
diff --git a/assets/argocd_bluegreen5.png b/assets/argocd_bluegreen5.png
new file mode 100644
index 0000000..71e4670
Binary files /dev/null and b/assets/argocd_bluegreen5.png differ
diff --git a/assets/argocd_bluegreen6.png b/assets/argocd_bluegreen6.png
new file mode 100644
index 0000000..ba03ef6
Binary files /dev/null and b/assets/argocd_bluegreen6.png differ
diff --git a/assets/argocd_bluegreen7.png b/assets/argocd_bluegreen7.png
new file mode 100644
index 0000000..3acf8d2
Binary files /dev/null and b/assets/argocd_bluegreen7.png differ
diff --git a/assets/argocd_bluegreen8.png b/assets/argocd_bluegreen8.png
new file mode 100644
index 0000000..6aae9bb
Binary files /dev/null and b/assets/argocd_bluegreen8.png differ
diff --git a/assets/argocd_bluegreen_resume.png b/assets/argocd_bluegreen_resume.png
new file mode 100644
index 0000000..6b02931
Binary files /dev/null and b/assets/argocd_bluegreen_resume.png differ
diff --git a/assets/argocd_bluegreen_resume_action.png b/assets/argocd_bluegreen_resume_action.png
new file mode 100644
index 0000000..5a11ee7
Binary files /dev/null and b/assets/argocd_bluegreen_resume_action.png differ
diff --git a/assets/argocd_canary1.png b/assets/argocd_canary1.png
new file mode 100644
index 0000000..1949919
Binary files /dev/null and b/assets/argocd_canary1.png differ
diff --git a/assets/argocd_canary2.png b/assets/argocd_canary2.png
new file mode 100644
index 0000000..d194f9f
Binary files /dev/null and b/assets/argocd_canary2.png differ
diff --git a/assets/argocd_canary3.png b/assets/argocd_canary3.png
new file mode 100644
index 0000000..3287fea
Binary files /dev/null and b/assets/argocd_canary3.png differ
diff --git a/assets/argocd_canary4.png b/assets/argocd_canary4.png
new file mode 100644
index 0000000..a8cce2c
Binary files /dev/null and b/assets/argocd_canary4.png differ
diff --git a/assets/argocd_canary5.png b/assets/argocd_canary5.png
new file mode 100644
index 0000000..0b98e20
Binary files /dev/null and b/assets/argocd_canary5.png differ
diff --git a/assets/argocd_canary6.png b/assets/argocd_canary6.png
new file mode 100644
index 0000000..5631390
Binary files /dev/null and b/assets/argocd_canary6.png differ
diff --git a/assets/argocd_canary7.png b/assets/argocd_canary7.png
new file mode 100644
index 0000000..6bbf023
Binary files /dev/null and b/assets/argocd_canary7.png differ
diff --git a/assets/argocd_edit_svc_after.png b/assets/argocd_edit_svc_after.png
new file mode 100644
index 0000000..941e6ee
Binary files /dev/null and b/assets/argocd_edit_svc_after.png differ
diff --git a/assets/argocd_edit_svc_before.png b/assets/argocd_edit_svc_before.png
new file mode 100644
index 0000000..f777ccf
Binary files /dev/null and b/assets/argocd_edit_svc_before.png differ
diff --git a/assets/argocd_edit_svc_finish.png b/assets/argocd_edit_svc_finish.png
new file mode 100644
index 0000000..1ec5e91
Binary files /dev/null and b/assets/argocd_edit_svc_finish.png differ
diff --git a/assets/argocd_ingress.png b/assets/argocd_ingress.png
new file mode 100644
index 0000000..e1a9337
Binary files /dev/null and b/assets/argocd_ingress.png differ
diff --git a/assets/argocd_install1.png b/assets/argocd_install1.png
new file mode 100644
index 0000000..ccbf54b
Binary files /dev/null and b/assets/argocd_install1.png differ
diff --git a/assets/argocd_install2.png b/assets/argocd_install2.png
new file mode 100644
index 0000000..d18639d
Binary files /dev/null and b/assets/argocd_install2.png differ
diff --git a/assets/argocd_install3.png b/assets/argocd_install3.png
new file mode 100644
index 0000000..3c2e958
Binary files /dev/null and b/assets/argocd_install3.png differ
diff --git a/assets/argocd_install4.png b/assets/argocd_install4.png
new file mode 100644
index 0000000..7c19590
Binary files /dev/null and b/assets/argocd_install4.png differ
diff --git a/assets/argocd_install5.png b/assets/argocd_install5.png
new file mode 100644
index 0000000..9963704
Binary files /dev/null and b/assets/argocd_install5.png differ
diff --git a/assets/argocd_mark.png b/assets/argocd_mark.png
new file mode 100644
index 0000000..c775df0
Binary files /dev/null and b/assets/argocd_mark.png differ
diff --git a/assets/argocd_new_account.png b/assets/argocd_new_account.png
new file mode 100644
index 0000000..be8ea32
Binary files /dev/null and b/assets/argocd_new_account.png differ
diff --git a/assets/argocd_new_account_role.png b/assets/argocd_new_account_role.png
new file mode 100644
index 0000000..9d28216
Binary files /dev/null and b/assets/argocd_new_account_role.png differ
diff --git a/assets/argocd_newapp1.png b/assets/argocd_newapp1.png
new file mode 100644
index 0000000..481e493
Binary files /dev/null and b/assets/argocd_newapp1.png differ
diff --git a/assets/argocd_newapp2.png b/assets/argocd_newapp2.png
new file mode 100644
index 0000000..032ca0c
Binary files /dev/null and b/assets/argocd_newapp2.png differ
diff --git a/assets/argocd_practice1.png b/assets/argocd_practice1.png
new file mode 100644
index 0000000..94da6b7
Binary files /dev/null and b/assets/argocd_practice1.png differ
diff --git a/assets/argocd_practice10.png b/assets/argocd_practice10.png
new file mode 100644
index 0000000..9e9ca52
Binary files /dev/null and b/assets/argocd_practice10.png differ
diff --git a/assets/argocd_practice2.png b/assets/argocd_practice2.png
new file mode 100644
index 0000000..ef5420e
Binary files /dev/null and b/assets/argocd_practice2.png differ
diff --git a/assets/argocd_practice3.png b/assets/argocd_practice3.png
new file mode 100644
index 0000000..9f6e3ad
Binary files /dev/null and b/assets/argocd_practice3.png differ
diff --git a/assets/argocd_practice4.png b/assets/argocd_practice4.png
new file mode 100644
index 0000000..fef434b
Binary files /dev/null and b/assets/argocd_practice4.png differ
diff --git a/assets/argocd_practice5.png b/assets/argocd_practice5.png
new file mode 100644
index 0000000..d2b76b0
Binary files /dev/null and b/assets/argocd_practice5.png differ
diff --git a/assets/argocd_practice6.png b/assets/argocd_practice6.png
new file mode 100644
index 0000000..0525d13
Binary files /dev/null and b/assets/argocd_practice6.png differ
diff --git a/assets/argocd_practice7.png b/assets/argocd_practice7.png
new file mode 100644
index 0000000..531d7ab
Binary files /dev/null and b/assets/argocd_practice7.png differ
diff --git a/assets/argocd_practice8.png b/assets/argocd_practice8.png
new file mode 100644
index 0000000..b1c28a3
Binary files /dev/null and b/assets/argocd_practice8.png differ
diff --git a/assets/argocd_practice9.png b/assets/argocd_practice9.png
new file mode 100644
index 0000000..fc5ea2d
Binary files /dev/null and b/assets/argocd_practice9.png differ
diff --git a/assets/argocd_remote_cluster_add.png b/assets/argocd_remote_cluster_add.png
new file mode 100644
index 0000000..bb9046e
Binary files /dev/null and b/assets/argocd_remote_cluster_add.png differ
diff --git a/assets/argocd_swagger.png b/assets/argocd_swagger.png
new file mode 100644
index 0000000..941e62b
Binary files /dev/null and b/assets/argocd_swagger.png differ
diff --git a/assets/argocd_update_password.png b/assets/argocd_update_password.png
new file mode 100644
index 0000000..f6b97f3
Binary files /dev/null and b/assets/argocd_update_password.png differ
diff --git a/assets/argocd_web_start.png b/assets/argocd_web_start.png
new file mode 100644
index 0000000..3d7d818
Binary files /dev/null and b/assets/argocd_web_start.png differ
diff --git a/assets/auto_import.png b/assets/auto_import.png
new file mode 100644
index 0000000..3f52cde
Binary files /dev/null and b/assets/auto_import.png differ
diff --git a/assets/blue_green_overview.png b/assets/blue_green_overview.png
new file mode 100644
index 0000000..43a9c58
Binary files /dev/null and b/assets/blue_green_overview.png differ
diff --git a/assets/build_again.png b/assets/build_again.png
new file mode 100644
index 0000000..b95502c
Binary files /dev/null and b/assets/build_again.png differ
diff --git a/assets/build_console_output.png b/assets/build_console_output.png
new file mode 100644
index 0000000..bf9d535
Binary files /dev/null and b/assets/build_console_output.png differ
diff --git a/assets/build_docker_access_denied.png b/assets/build_docker_access_denied.png
new file mode 100644
index 0000000..705618b
Binary files /dev/null and b/assets/build_docker_access_denied.png differ
diff --git a/assets/build_dockerhub_check.png b/assets/build_dockerhub_check.png
new file mode 100644
index 0000000..d9e5098
Binary files /dev/null and b/assets/build_dockerhub_check.png differ
diff --git a/assets/build_error.png b/assets/build_error.png
new file mode 100644
index 0000000..7cd10f2
Binary files /dev/null and b/assets/build_error.png differ
diff --git a/assets/build_finish.png b/assets/build_finish.png
new file mode 100644
index 0000000..547e7a8
Binary files /dev/null and b/assets/build_finish.png differ
diff --git a/assets/build_history.png b/assets/build_history.png
new file mode 100644
index 0000000..1962735
Binary files /dev/null and b/assets/build_history.png differ
diff --git a/assets/build_stage1.png b/assets/build_stage1.png
new file mode 100644
index 0000000..9fd68de
Binary files /dev/null and b/assets/build_stage1.png differ
diff --git a/assets/build_stage2.png b/assets/build_stage2.png
new file mode 100644
index 0000000..c3f6b1a
Binary files /dev/null and b/assets/build_stage2.png differ
diff --git a/assets/canary_config.png b/assets/canary_config.png
new file mode 100644
index 0000000..83f7170
Binary files /dev/null and b/assets/canary_config.png differ
diff --git a/assets/canary_overview.png b/assets/canary_overview.png
new file mode 100644
index 0000000..b8d60b8
Binary files /dev/null and b/assets/canary_overview.png differ
diff --git a/assets/cat_k3s_yaml.png b/assets/cat_k3s_yaml.png
new file mode 100644
index 0000000..56f9c02
Binary files /dev/null and b/assets/cat_k3s_yaml.png differ
diff --git a/assets/chapter2-2-answer.png b/assets/chapter2-2-answer.png
new file mode 100644
index 0000000..9b1c31a
Binary files /dev/null and b/assets/chapter2-2-answer.png differ
diff --git a/assets/chapter2-2-answer2.png b/assets/chapter2-2-answer2.png
new file mode 100644
index 0000000..4d7afc5
Binary files /dev/null and b/assets/chapter2-2-answer2.png differ
diff --git a/assets/chrome_extension_pin.png b/assets/chrome_extension_pin.png
new file mode 100644
index 0000000..c17d6a9
Binary files /dev/null and b/assets/chrome_extension_pin.png differ
diff --git a/assets/chrome_extensions.png b/assets/chrome_extensions.png
new file mode 100644
index 0000000..c5cb95a
Binary files /dev/null and b/assets/chrome_extensions.png differ
diff --git a/assets/cluster_info.png b/assets/cluster_info.png
new file mode 100644
index 0000000..a5d7669
Binary files /dev/null and b/assets/cluster_info.png differ
diff --git a/assets/config_merge.png b/assets/config_merge.png
new file mode 100644
index 0000000..c1a131e
Binary files /dev/null and b/assets/config_merge.png differ
diff --git a/assets/configure_global_security.png b/assets/configure_global_security.png
new file mode 100644
index 0000000..2b4f6b3
Binary files /dev/null and b/assets/configure_global_security.png differ
diff --git a/assets/configure_global_security2.png b/assets/configure_global_security2.png
new file mode 100644
index 0000000..fe51636
Binary files /dev/null and b/assets/configure_global_security2.png differ
diff --git a/assets/container_portal.png b/assets/container_portal.png
new file mode 100644
index 0000000..22ed4b7
Binary files /dev/null and b/assets/container_portal.png differ
diff --git a/assets/container_portal1.png b/assets/container_portal1.png
new file mode 100644
index 0000000..d729210
Binary files /dev/null and b/assets/container_portal1.png differ
diff --git a/assets/crud1.png b/assets/crud1.png
new file mode 100644
index 0000000..894e433
Binary files /dev/null and b/assets/crud1.png differ
diff --git a/assets/crud2.png b/assets/crud2.png
new file mode 100644
index 0000000..1c74a67
Binary files /dev/null and b/assets/crud2.png differ
diff --git a/assets/crud3.png b/assets/crud3.png
new file mode 100644
index 0000000..1ddc417
Binary files /dev/null and b/assets/crud3.png differ
diff --git a/assets/datadog_apikey_find1.png b/assets/datadog_apikey_find1.png
new file mode 100644
index 0000000..6de0712
Binary files /dev/null and b/assets/datadog_apikey_find1.png differ
diff --git a/assets/datadog_apikey_find2.png b/assets/datadog_apikey_find2.png
new file mode 100644
index 0000000..6a1ed86
Binary files /dev/null and b/assets/datadog_apikey_find2.png differ
diff --git a/assets/datadog_apikey_find3.png b/assets/datadog_apikey_find3.png
new file mode 100644
index 0000000..a8ab7b6
Binary files /dev/null and b/assets/datadog_apikey_find3.png differ
diff --git a/assets/datadog_apikey_find4.png b/assets/datadog_apikey_find4.png
new file mode 100644
index 0000000..b9fed2e
Binary files /dev/null and b/assets/datadog_apikey_find4.png differ
diff --git a/assets/datadog_apm_trace.png b/assets/datadog_apm_trace.png
new file mode 100644
index 0000000..f54067c
Binary files /dev/null and b/assets/datadog_apm_trace.png differ
diff --git a/assets/datadog_apm_trace1.png b/assets/datadog_apm_trace1.png
new file mode 100644
index 0000000..2bd0dfa
Binary files /dev/null and b/assets/datadog_apm_trace1.png differ
diff --git a/assets/datadog_apm_trace2.png b/assets/datadog_apm_trace2.png
new file mode 100644
index 0000000..c7dbd2b
Binary files /dev/null and b/assets/datadog_apm_trace2.png differ
diff --git a/assets/datadog_appkey1.png b/assets/datadog_appkey1.png
new file mode 100644
index 0000000..62a0816
Binary files /dev/null and b/assets/datadog_appkey1.png differ
diff --git a/assets/datadog_appkey2.png b/assets/datadog_appkey2.png
new file mode 100644
index 0000000..317e105
Binary files /dev/null and b/assets/datadog_appkey2.png differ
diff --git a/assets/datadog_appkey3.png b/assets/datadog_appkey3.png
new file mode 100644
index 0000000..347012a
Binary files /dev/null and b/assets/datadog_appkey3.png differ
diff --git a/assets/datadog_container_trace.png b/assets/datadog_container_trace.png
new file mode 100644
index 0000000..4b25a61
Binary files /dev/null and b/assets/datadog_container_trace.png differ
diff --git a/assets/datadog_docs1.png b/assets/datadog_docs1.png
new file mode 100644
index 0000000..f31b129
Binary files /dev/null and b/assets/datadog_docs1.png differ
diff --git a/assets/datadog_docs2.png b/assets/datadog_docs2.png
new file mode 100644
index 0000000..df3d850
Binary files /dev/null and b/assets/datadog_docs2.png differ
diff --git a/assets/datadog_docs3.png b/assets/datadog_docs3.png
new file mode 100644
index 0000000..61cf89a
Binary files /dev/null and b/assets/datadog_docs3.png differ
diff --git a/assets/datadog_dogstatsd.png b/assets/datadog_dogstatsd.png
new file mode 100644
index 0000000..fba2c84
Binary files /dev/null and b/assets/datadog_dogstatsd.png differ
diff --git a/assets/datadog_home.png b/assets/datadog_home.png
new file mode 100644
index 0000000..3b579a5
Binary files /dev/null and b/assets/datadog_home.png differ
diff --git a/assets/datadog_infra_list.png b/assets/datadog_infra_list.png
new file mode 100644
index 0000000..4676fcf
Binary files /dev/null and b/assets/datadog_infra_list.png differ
diff --git a/assets/datadog_infra_server.png b/assets/datadog_infra_server.png
new file mode 100644
index 0000000..6f83074
Binary files /dev/null and b/assets/datadog_infra_server.png differ
diff --git a/assets/datadog_infra_server_container.png b/assets/datadog_infra_server_container.png
new file mode 100644
index 0000000..9d242a0
Binary files /dev/null and b/assets/datadog_infra_server_container.png differ
diff --git a/assets/datadog_infra_server_detail.png b/assets/datadog_infra_server_detail.png
new file mode 100644
index 0000000..88223ec
Binary files /dev/null and b/assets/datadog_infra_server_detail.png differ
diff --git a/assets/datadog_infra_trace.png b/assets/datadog_infra_trace.png
new file mode 100644
index 0000000..e34d8ca
Binary files /dev/null and b/assets/datadog_infra_trace.png differ
diff --git a/assets/datadog_k8s_container.png b/assets/datadog_k8s_container.png
new file mode 100644
index 0000000..0f438c4
Binary files /dev/null and b/assets/datadog_k8s_container.png differ
diff --git a/assets/datadog_k8s_container_prev.png b/assets/datadog_k8s_container_prev.png
new file mode 100644
index 0000000..d31fe9e
Binary files /dev/null and b/assets/datadog_k8s_container_prev.png differ
diff --git a/assets/datadog_select_zone.png b/assets/datadog_select_zone.png
new file mode 100644
index 0000000..2a1fe87
Binary files /dev/null and b/assets/datadog_select_zone.png differ
diff --git a/assets/datadog_signup.png b/assets/datadog_signup.png
new file mode 100644
index 0000000..132a82a
Binary files /dev/null and b/assets/datadog_signup.png differ
diff --git a/assets/datadog_trace_collect.png b/assets/datadog_trace_collect.png
new file mode 100644
index 0000000..734094b
Binary files /dev/null and b/assets/datadog_trace_collect.png differ
diff --git a/assets/datadog_trace_error.png b/assets/datadog_trace_error.png
new file mode 100644
index 0000000..acb2def
Binary files /dev/null and b/assets/datadog_trace_error.png differ
diff --git a/assets/datadog_trace_vm1.png b/assets/datadog_trace_vm1.png
new file mode 100644
index 0000000..ffb10ff
Binary files /dev/null and b/assets/datadog_trace_vm1.png differ
diff --git a/assets/datadog_upgrade.png b/assets/datadog_upgrade.png
new file mode 100644
index 0000000..39927b0
Binary files /dev/null and b/assets/datadog_upgrade.png differ
diff --git a/assets/datagog_k8s_architecture.png b/assets/datagog_k8s_architecture.png
new file mode 100644
index 0000000..e819496
Binary files /dev/null and b/assets/datagog_k8s_architecture.png differ
diff --git a/assets/db_summary.png b/assets/db_summary.png
new file mode 100644
index 0000000..1c6594d
Binary files /dev/null and b/assets/db_summary.png differ
diff --git a/assets/default_branch_modify.png b/assets/default_branch_modify.png
new file mode 100644
index 0000000..c1187a1
Binary files /dev/null and b/assets/default_branch_modify.png differ
diff --git a/assets/delete1.png b/assets/delete1.png
new file mode 100644
index 0000000..f0bde82
Binary files /dev/null and b/assets/delete1.png differ
diff --git a/assets/delete2-1.png b/assets/delete2-1.png
new file mode 100644
index 0000000..ee84cca
Binary files /dev/null and b/assets/delete2-1.png differ
diff --git a/assets/delete2.png b/assets/delete2.png
new file mode 100644
index 0000000..72c1596
Binary files /dev/null and b/assets/delete2.png differ
diff --git a/assets/delete3.png b/assets/delete3.png
new file mode 100644
index 0000000..8f43d03
Binary files /dev/null and b/assets/delete3.png differ
diff --git a/assets/delete4.png b/assets/delete4.png
new file mode 100644
index 0000000..42eee0b
Binary files /dev/null and b/assets/delete4.png differ
diff --git a/assets/delete5.png b/assets/delete5.png
new file mode 100644
index 0000000..c6a4f76
Binary files /dev/null and b/assets/delete5.png differ
diff --git a/assets/demo_replica.png b/assets/demo_replica.png
new file mode 100644
index 0000000..1d178ed
Binary files /dev/null and b/assets/demo_replica.png differ
diff --git a/assets/demo_replica2.png b/assets/demo_replica2.png
new file mode 100644
index 0000000..77d7ab8
Binary files /dev/null and b/assets/demo_replica2.png differ
diff --git a/assets/deployment.png b/assets/deployment.png
new file mode 100644
index 0000000..2ac9f9e
Binary files /dev/null and b/assets/deployment.png differ
diff --git a/assets/disk_add1.png b/assets/disk_add1.png
new file mode 100644
index 0000000..a7f2460
Binary files /dev/null and b/assets/disk_add1.png differ
diff --git a/assets/disk_add2.png b/assets/disk_add2.png
new file mode 100644
index 0000000..deb081d
Binary files /dev/null and b/assets/disk_add2.png differ
diff --git a/assets/disk_add3.png b/assets/disk_add3.png
new file mode 100644
index 0000000..27645b0
Binary files /dev/null and b/assets/disk_add3.png differ
diff --git a/assets/disk_add4.png b/assets/disk_add4.png
new file mode 100644
index 0000000..a443591
Binary files /dev/null and b/assets/disk_add4.png differ
diff --git a/assets/disk_add5.png b/assets/disk_add5.png
new file mode 100644
index 0000000..1bc3c54
Binary files /dev/null and b/assets/disk_add5.png differ
diff --git a/assets/disk_add6.png b/assets/disk_add6.png
new file mode 100644
index 0000000..df58ee5
Binary files /dev/null and b/assets/disk_add6.png differ
diff --git a/assets/disk_add7.png b/assets/disk_add7.png
new file mode 100644
index 0000000..9182ce3
Binary files /dev/null and b/assets/disk_add7.png differ
diff --git a/assets/disk_add_fdisk.png b/assets/disk_add_fdisk.png
new file mode 100644
index 0000000..8f48ca4
Binary files /dev/null and b/assets/disk_add_fdisk.png differ
diff --git a/assets/disk_mount.png b/assets/disk_mount.png
new file mode 100644
index 0000000..f1407b1
Binary files /dev/null and b/assets/disk_mount.png differ
diff --git a/assets/docker_build_t1.png b/assets/docker_build_t1.png
new file mode 100644
index 0000000..9092b1f
Binary files /dev/null and b/assets/docker_build_t1.png differ
diff --git a/assets/docker_build_t2.png b/assets/docker_build_t2.png
new file mode 100644
index 0000000..38c3e70
Binary files /dev/null and b/assets/docker_build_t2.png differ
diff --git a/assets/docker_commit.png b/assets/docker_commit.png
new file mode 100644
index 0000000..d964d61
Binary files /dev/null and b/assets/docker_commit.png differ
diff --git a/assets/docker_compose_install.png b/assets/docker_compose_install.png
new file mode 100644
index 0000000..1609461
Binary files /dev/null and b/assets/docker_compose_install.png differ
diff --git a/assets/docker_compose_ls.png b/assets/docker_compose_ls.png
new file mode 100644
index 0000000..30140dc
Binary files /dev/null and b/assets/docker_compose_ls.png differ
diff --git a/assets/docker_compose_mkdir.png b/assets/docker_compose_mkdir.png
new file mode 100644
index 0000000..1b1d156
Binary files /dev/null and b/assets/docker_compose_mkdir.png differ
diff --git a/assets/docker_compose_ps.png b/assets/docker_compose_ps.png
new file mode 100644
index 0000000..1f58cc8
Binary files /dev/null and b/assets/docker_compose_ps.png differ
diff --git a/assets/docker_compose_up.png b/assets/docker_compose_up.png
new file mode 100644
index 0000000..cd6c14f
Binary files /dev/null and b/assets/docker_compose_up.png differ
diff --git a/assets/docker_compose_version.png b/assets/docker_compose_version.png
new file mode 100644
index 0000000..c27db5e
Binary files /dev/null and b/assets/docker_compose_version.png differ
diff --git a/assets/docker_compose_volume.png b/assets/docker_compose_volume.png
new file mode 100644
index 0000000..c70b4d9
Binary files /dev/null and b/assets/docker_compose_volume.png differ
diff --git a/assets/docker_compose_web.png b/assets/docker_compose_web.png
new file mode 100644
index 0000000..c51b31e
Binary files /dev/null and b/assets/docker_compose_web.png differ
diff --git a/assets/docker_denied.png b/assets/docker_denied.png
new file mode 100644
index 0000000..b1a7494
Binary files /dev/null and b/assets/docker_denied.png differ
diff --git a/assets/docker_edu2_push.png b/assets/docker_edu2_push.png
new file mode 100644
index 0000000..a40f0d0
Binary files /dev/null and b/assets/docker_edu2_push.png differ
diff --git a/assets/docker_exec.png b/assets/docker_exec.png
new file mode 100644
index 0000000..abbe181
Binary files /dev/null and b/assets/docker_exec.png differ
diff --git a/assets/docker_hub_make_public.png b/assets/docker_hub_make_public.png
new file mode 100644
index 0000000..04f100d
Binary files /dev/null and b/assets/docker_hub_make_public.png differ
diff --git a/assets/docker_hub_world.png b/assets/docker_hub_world.png
new file mode 100644
index 0000000..6b600de
Binary files /dev/null and b/assets/docker_hub_world.png differ
diff --git a/assets/docker_images.png b/assets/docker_images.png
new file mode 100644
index 0000000..cdb79bb
Binary files /dev/null and b/assets/docker_images.png differ
diff --git a/assets/docker_logs.png b/assets/docker_logs.png
new file mode 100644
index 0000000..93ec2ad
Binary files /dev/null and b/assets/docker_logs.png differ
diff --git a/assets/docker_overview.png b/assets/docker_overview.png
new file mode 100644
index 0000000..355aba3
Binary files /dev/null and b/assets/docker_overview.png differ
diff --git a/assets/docker_ps.png b/assets/docker_ps.png
new file mode 100644
index 0000000..b9404df
Binary files /dev/null and b/assets/docker_ps.png differ
diff --git a/assets/docker_ps_stop.png b/assets/docker_ps_stop.png
new file mode 100644
index 0000000..5c1fc1f
Binary files /dev/null and b/assets/docker_ps_stop.png differ
diff --git a/assets/docker_pull_edu1.png b/assets/docker_pull_edu1.png
new file mode 100644
index 0000000..4765c43
Binary files /dev/null and b/assets/docker_pull_edu1.png differ
diff --git a/assets/docker_push.png b/assets/docker_push.png
new file mode 100644
index 0000000..b914b42
Binary files /dev/null and b/assets/docker_push.png differ
diff --git a/assets/docker_root_dir.png b/assets/docker_root_dir.png
new file mode 100644
index 0000000..0b9173b
Binary files /dev/null and b/assets/docker_root_dir.png differ
diff --git a/assets/docker_run_d.png b/assets/docker_run_d.png
new file mode 100644
index 0000000..1927f99
Binary files /dev/null and b/assets/docker_run_d.png differ
diff --git a/assets/docker_run_edu1.png b/assets/docker_run_edu1.png
new file mode 100644
index 0000000..860799b
Binary files /dev/null and b/assets/docker_run_edu1.png differ
diff --git a/assets/docker_run_world.png b/assets/docker_run_world.png
new file mode 100644
index 0000000..056eb80
Binary files /dev/null and b/assets/docker_run_world.png differ
diff --git a/assets/docker_stats.png b/assets/docker_stats.png
new file mode 100644
index 0000000..2e72c60
Binary files /dev/null and b/assets/docker_stats.png differ
diff --git a/assets/docker_system_df_v.png b/assets/docker_system_df_v.png
new file mode 100644
index 0000000..e83a9e4
Binary files /dev/null and b/assets/docker_system_df_v.png differ
diff --git a/assets/docker_version.png b/assets/docker_version.png
new file mode 100644
index 0000000..3d8b9ab
Binary files /dev/null and b/assets/docker_version.png differ
diff --git a/assets/dockerhub_edu2_image.png b/assets/dockerhub_edu2_image.png
new file mode 100644
index 0000000..1df92ee
Binary files /dev/null and b/assets/dockerhub_edu2_image.png differ
diff --git a/assets/downward_api_pod.png b/assets/downward_api_pod.png
new file mode 100644
index 0000000..ccb177e
Binary files /dev/null and b/assets/downward_api_pod.png differ
diff --git a/assets/dto_package_create.png b/assets/dto_package_create.png
new file mode 100644
index 0000000..23ed269
Binary files /dev/null and b/assets/dto_package_create.png differ
diff --git a/assets/du_h_overlay.png b/assets/du_h_overlay.png
new file mode 100644
index 0000000..fa8855a
Binary files /dev/null and b/assets/du_h_overlay.png differ
diff --git a/assets/file_encoding.png b/assets/file_encoding.png
new file mode 100644
index 0000000..02cdd1a
Binary files /dev/null and b/assets/file_encoding.png differ
diff --git a/assets/first_login1.png b/assets/first_login1.png
new file mode 100644
index 0000000..849f17f
Binary files /dev/null and b/assets/first_login1.png differ
diff --git a/assets/first_login2.png b/assets/first_login2.png
new file mode 100644
index 0000000..0e59e80
Binary files /dev/null and b/assets/first_login2.png differ
diff --git a/assets/flask_web_edu1.png b/assets/flask_web_edu1.png
new file mode 100644
index 0000000..ca975ec
Binary files /dev/null and b/assets/flask_web_edu1.png differ
diff --git a/assets/git_clone.png b/assets/git_clone.png
new file mode 100644
index 0000000..d72767c
Binary files /dev/null and b/assets/git_clone.png differ
diff --git a/assets/git_push.png b/assets/git_push.png
new file mode 100644
index 0000000..290329e
Binary files /dev/null and b/assets/git_push.png differ
diff --git a/assets/git_push_github.png b/assets/git_push_github.png
new file mode 100644
index 0000000..6fe6146
Binary files /dev/null and b/assets/git_push_github.png differ
diff --git a/assets/git_remote.png b/assets/git_remote.png
new file mode 100644
index 0000000..81149e2
Binary files /dev/null and b/assets/git_remote.png differ
diff --git a/assets/git_remote_modify.png b/assets/git_remote_modify.png
new file mode 100644
index 0000000..a889697
Binary files /dev/null and b/assets/git_remote_modify.png differ
diff --git a/assets/git_version.png b/assets/git_version.png
new file mode 100644
index 0000000..d28b848
Binary files /dev/null and b/assets/git_version.png differ
diff --git a/assets/github_action1.png b/assets/github_action1.png
new file mode 100644
index 0000000..171d9dd
Binary files /dev/null and b/assets/github_action1.png differ
diff --git a/assets/github_action10.png b/assets/github_action10.png
new file mode 100644
index 0000000..6fd02e2
Binary files /dev/null and b/assets/github_action10.png differ
diff --git a/assets/github_action11.png b/assets/github_action11.png
new file mode 100644
index 0000000..76322c0
Binary files /dev/null and b/assets/github_action11.png differ
diff --git a/assets/github_action12.png b/assets/github_action12.png
new file mode 100644
index 0000000..ff1851b
Binary files /dev/null and b/assets/github_action12.png differ
diff --git a/assets/github_action13.png b/assets/github_action13.png
new file mode 100644
index 0000000..8e20b8e
Binary files /dev/null and b/assets/github_action13.png differ
diff --git a/assets/github_action14.png b/assets/github_action14.png
new file mode 100644
index 0000000..c199dee
Binary files /dev/null and b/assets/github_action14.png differ
diff --git a/assets/github_action15.png b/assets/github_action15.png
new file mode 100644
index 0000000..60ea4c3
Binary files /dev/null and b/assets/github_action15.png differ
diff --git a/assets/github_action3.png b/assets/github_action3.png
new file mode 100644
index 0000000..5099779
Binary files /dev/null and b/assets/github_action3.png differ
diff --git a/assets/github_action4.png b/assets/github_action4.png
new file mode 100644
index 0000000..d15babd
Binary files /dev/null and b/assets/github_action4.png differ
diff --git a/assets/github_action5.png b/assets/github_action5.png
new file mode 100644
index 0000000..7ef7676
Binary files /dev/null and b/assets/github_action5.png differ
diff --git a/assets/github_action6.png b/assets/github_action6.png
new file mode 100644
index 0000000..8d1095e
Binary files /dev/null and b/assets/github_action6.png differ
diff --git a/assets/github_action7.png b/assets/github_action7.png
new file mode 100644
index 0000000..1aa141e
Binary files /dev/null and b/assets/github_action7.png differ
diff --git a/assets/github_action8-1.png b/assets/github_action8-1.png
new file mode 100644
index 0000000..6190171
Binary files /dev/null and b/assets/github_action8-1.png differ
diff --git a/assets/github_action8.png b/assets/github_action8.png
new file mode 100644
index 0000000..05aae0d
Binary files /dev/null and b/assets/github_action8.png differ
diff --git a/assets/github_action9.png b/assets/github_action9.png
new file mode 100644
index 0000000..d9a45ad
Binary files /dev/null and b/assets/github_action9.png differ
diff --git a/assets/github_action_docker1.png b/assets/github_action_docker1.png
new file mode 100644
index 0000000..74d82f2
Binary files /dev/null and b/assets/github_action_docker1.png differ
diff --git a/assets/github_action_docker10.png b/assets/github_action_docker10.png
new file mode 100644
index 0000000..3078619
Binary files /dev/null and b/assets/github_action_docker10.png differ
diff --git a/assets/github_action_docker2.png b/assets/github_action_docker2.png
new file mode 100644
index 0000000..2d687d8
Binary files /dev/null and b/assets/github_action_docker2.png differ
diff --git a/assets/github_action_docker3.png b/assets/github_action_docker3.png
new file mode 100644
index 0000000..a6c636e
Binary files /dev/null and b/assets/github_action_docker3.png differ
diff --git a/assets/github_action_docker4.png b/assets/github_action_docker4.png
new file mode 100644
index 0000000..af109d8
Binary files /dev/null and b/assets/github_action_docker4.png differ
diff --git a/assets/github_action_docker5.png b/assets/github_action_docker5.png
new file mode 100644
index 0000000..f582d4b
Binary files /dev/null and b/assets/github_action_docker5.png differ
diff --git a/assets/github_action_docker6.png b/assets/github_action_docker6.png
new file mode 100644
index 0000000..136e935
Binary files /dev/null and b/assets/github_action_docker6.png differ
diff --git a/assets/github_action_docker7.png b/assets/github_action_docker7.png
new file mode 100644
index 0000000..441232a
Binary files /dev/null and b/assets/github_action_docker7.png differ
diff --git a/assets/github_action_docker8.png b/assets/github_action_docker8.png
new file mode 100644
index 0000000..500ec34
Binary files /dev/null and b/assets/github_action_docker8.png differ
diff --git a/assets/github_action_docker9.png b/assets/github_action_docker9.png
new file mode 100644
index 0000000..7e8032c
Binary files /dev/null and b/assets/github_action_docker9.png differ
diff --git a/assets/github_action_manual.png b/assets/github_action_manual.png
new file mode 100644
index 0000000..8c7273b
Binary files /dev/null and b/assets/github_action_manual.png differ
diff --git a/assets/github_action_template.png b/assets/github_action_template.png
new file mode 100644
index 0000000..dd38448
Binary files /dev/null and b/assets/github_action_template.png differ
diff --git a/assets/github_clone.png b/assets/github_clone.png
new file mode 100644
index 0000000..941d7a4
Binary files /dev/null and b/assets/github_clone.png differ
diff --git a/assets/github_edu2.png b/assets/github_edu2.png
new file mode 100644
index 0000000..43327b9
Binary files /dev/null and b/assets/github_edu2.png differ
diff --git a/assets/github_package.png b/assets/github_package.png
new file mode 100644
index 0000000..17256d7
Binary files /dev/null and b/assets/github_package.png differ
diff --git a/assets/github_tag1.png b/assets/github_tag1.png
new file mode 100644
index 0000000..27a4a4d
Binary files /dev/null and b/assets/github_tag1.png differ
diff --git a/assets/github_tag2.png b/assets/github_tag2.png
new file mode 100644
index 0000000..2cc8b94
Binary files /dev/null and b/assets/github_tag2.png differ
diff --git a/assets/github_tag3.png b/assets/github_tag3.png
new file mode 100644
index 0000000..97fc623
Binary files /dev/null and b/assets/github_tag3.png differ
diff --git a/assets/github_tag4.png b/assets/github_tag4.png
new file mode 100644
index 0000000..d651c51
Binary files /dev/null and b/assets/github_tag4.png differ
diff --git a/assets/github_tag5.png b/assets/github_tag5.png
new file mode 100644
index 0000000..901b21c
Binary files /dev/null and b/assets/github_tag5.png differ
diff --git a/assets/github_tag6.png b/assets/github_tag6.png
new file mode 100644
index 0000000..c82332d
Binary files /dev/null and b/assets/github_tag6.png differ
diff --git a/assets/github_token_setting1.png b/assets/github_token_setting1.png
new file mode 100644
index 0000000..4136f2a
Binary files /dev/null and b/assets/github_token_setting1.png differ
diff --git a/assets/github_token_setting2.png b/assets/github_token_setting2.png
new file mode 100644
index 0000000..ce37cd4
Binary files /dev/null and b/assets/github_token_setting2.png differ
diff --git a/assets/github_token_setting3.png b/assets/github_token_setting3.png
new file mode 100644
index 0000000..3a63aa0
Binary files /dev/null and b/assets/github_token_setting3.png differ
diff --git a/assets/github_web_hook.png b/assets/github_web_hook.png
new file mode 100644
index 0000000..4ababac
Binary files /dev/null and b/assets/github_web_hook.png differ
diff --git a/assets/gitops_overview.png b/assets/gitops_overview.png
new file mode 100644
index 0000000..0a89163
Binary files /dev/null and b/assets/gitops_overview.png differ
diff --git a/assets/gitops_pipeline.png b/assets/gitops_pipeline.png
new file mode 100644
index 0000000..36400c2
Binary files /dev/null and b/assets/gitops_pipeline.png differ
diff --git a/assets/gitops_pull.png b/assets/gitops_pull.png
new file mode 100644
index 0000000..20b750f
Binary files /dev/null and b/assets/gitops_pull.png differ
diff --git a/assets/gitops_push.png b/assets/gitops_push.png
new file mode 100644
index 0000000..9cc04b4
Binary files /dev/null and b/assets/gitops_push.png differ
diff --git a/assets/h2db_auto_insert.png b/assets/h2db_auto_insert.png
new file mode 100644
index 0000000..efdf744
Binary files /dev/null and b/assets/h2db_auto_insert.png differ
diff --git a/assets/helm_install.png b/assets/helm_install.png
new file mode 100644
index 0000000..6479932
Binary files /dev/null and b/assets/helm_install.png differ
diff --git a/assets/helm_prometheus_install.png b/assets/helm_prometheus_install.png
new file mode 100644
index 0000000..2e51ccd
Binary files /dev/null and b/assets/helm_prometheus_install.png differ
diff --git a/assets/helm_repo_add.png b/assets/helm_repo_add.png
new file mode 100644
index 0000000..d3af07b
Binary files /dev/null and b/assets/helm_repo_add.png differ
diff --git a/assets/helm_search_prometheus.png b/assets/helm_search_prometheus.png
new file mode 100644
index 0000000..fb6eb02
Binary files /dev/null and b/assets/helm_search_prometheus.png differ
diff --git a/assets/helm_version.png b/assets/helm_version.png
new file mode 100644
index 0000000..1064d4a
Binary files /dev/null and b/assets/helm_version.png differ
diff --git a/assets/hibernate1.png b/assets/hibernate1.png
new file mode 100644
index 0000000..6f1ea36
Binary files /dev/null and b/assets/hibernate1.png differ
diff --git a/assets/ingress.png b/assets/ingress.png
new file mode 100644
index 0000000..8df7820
Binary files /dev/null and b/assets/ingress.png differ
diff --git a/assets/ingress_nginx.png b/assets/ingress_nginx.png
new file mode 100644
index 0000000..36fabb3
Binary files /dev/null and b/assets/ingress_nginx.png differ
diff --git a/assets/inspekt_deploy.png b/assets/inspekt_deploy.png
new file mode 100644
index 0000000..40e3676
Binary files /dev/null and b/assets/inspekt_deploy.png differ
diff --git a/assets/inspekt_traefik_ingress.png b/assets/inspekt_traefik_ingress.png
new file mode 100644
index 0000000..3ec619f
Binary files /dev/null and b/assets/inspekt_traefik_ingress.png differ
diff --git a/assets/install_k3s.png b/assets/install_k3s.png
new file mode 100644
index 0000000..98f5149
Binary files /dev/null and b/assets/install_k3s.png differ
diff --git a/assets/intellij_download_mac.png b/assets/intellij_download_mac.png
new file mode 100644
index 0000000..5c6ee1c
Binary files /dev/null and b/assets/intellij_download_mac.png differ
diff --git a/assets/intellij_welcome.png b/assets/intellij_welcome.png
new file mode 100644
index 0000000..2e423fc
Binary files /dev/null and b/assets/intellij_welcome.png differ
diff --git a/assets/istio.png b/assets/istio.png
new file mode 100644
index 0000000..d7790f9
Binary files /dev/null and b/assets/istio.png differ
diff --git a/assets/java17_mac1.png b/assets/java17_mac1.png
new file mode 100644
index 0000000..1e8473f
Binary files /dev/null and b/assets/java17_mac1.png differ
diff --git a/assets/java17_windows1.png b/assets/java17_windows1.png
new file mode 100644
index 0000000..259833a
Binary files /dev/null and b/assets/java17_windows1.png differ
diff --git a/assets/jdbc1.png b/assets/jdbc1.png
new file mode 100644
index 0000000..5bcb778
Binary files /dev/null and b/assets/jdbc1.png differ
diff --git a/assets/jenkins_admin_password.png b/assets/jenkins_admin_password.png
new file mode 100644
index 0000000..579f29a
Binary files /dev/null and b/assets/jenkins_admin_password.png differ
diff --git a/assets/jenkins_admin_user.png b/assets/jenkins_admin_user.png
new file mode 100644
index 0000000..24114c7
Binary files /dev/null and b/assets/jenkins_admin_user.png differ
diff --git a/assets/jenkins_admin_user_create.png b/assets/jenkins_admin_user_create.png
new file mode 100644
index 0000000..2578b95
Binary files /dev/null and b/assets/jenkins_admin_user_create.png differ
diff --git a/assets/jenkins_admin_user_created.png b/assets/jenkins_admin_user_created.png
new file mode 100644
index 0000000..771d667
Binary files /dev/null and b/assets/jenkins_admin_user_created.png differ
diff --git a/assets/jenkins_build_folder.png b/assets/jenkins_build_folder.png
new file mode 100644
index 0000000..09e9043
Binary files /dev/null and b/assets/jenkins_build_folder.png differ
diff --git a/assets/jenkins_build_history.png b/assets/jenkins_build_history.png
new file mode 100644
index 0000000..909f0c0
Binary files /dev/null and b/assets/jenkins_build_history.png differ
diff --git a/assets/jenkins_ci_start.png b/assets/jenkins_ci_start.png
new file mode 100644
index 0000000..c0bcdd9
Binary files /dev/null and b/assets/jenkins_ci_start.png differ
diff --git a/assets/jenkins_current_variable.png b/assets/jenkins_current_variable.png
new file mode 100644
index 0000000..5a42cb1
Binary files /dev/null and b/assets/jenkins_current_variable.png differ
diff --git a/assets/jenkins_dockerhub_credential.png b/assets/jenkins_dockerhub_credential.png
new file mode 100644
index 0000000..14b895c
Binary files /dev/null and b/assets/jenkins_dockerhub_credential.png differ
diff --git a/assets/jenkins_env_variable.png b/assets/jenkins_env_variable.png
new file mode 100644
index 0000000..e53227c
Binary files /dev/null and b/assets/jenkins_env_variable.png differ
diff --git a/assets/jenkins_first_build.png b/assets/jenkins_first_build.png
new file mode 100644
index 0000000..eac31eb
Binary files /dev/null and b/assets/jenkins_first_build.png differ
diff --git a/assets/jenkins_first_manage_plugins.png b/assets/jenkins_first_manage_plugins.png
new file mode 100644
index 0000000..b420cdc
Binary files /dev/null and b/assets/jenkins_first_manage_plugins.png differ
diff --git a/assets/jenkins_github_credential1.png b/assets/jenkins_github_credential1.png
new file mode 100644
index 0000000..4970ff0
Binary files /dev/null and b/assets/jenkins_github_credential1.png differ
diff --git a/assets/jenkins_github_credential2.png b/assets/jenkins_github_credential2.png
new file mode 100644
index 0000000..79f5ddc
Binary files /dev/null and b/assets/jenkins_github_credential2.png differ
diff --git a/assets/jenkins_github_credential3.png b/assets/jenkins_github_credential3.png
new file mode 100644
index 0000000..2f1da59
Binary files /dev/null and b/assets/jenkins_github_credential3.png differ
diff --git a/assets/jenkins_github_credential4.png b/assets/jenkins_github_credential4.png
new file mode 100644
index 0000000..1e0dba2
Binary files /dev/null and b/assets/jenkins_github_credential4.png differ
diff --git a/assets/jenkins_github_dockerhub_credential.png b/assets/jenkins_github_dockerhub_credential.png
new file mode 100644
index 0000000..e117e01
Binary files /dev/null and b/assets/jenkins_github_dockerhub_credential.png differ
diff --git a/assets/jenkins_home_folder.png b/assets/jenkins_home_folder.png
new file mode 100644
index 0000000..5c498f0
Binary files /dev/null and b/assets/jenkins_home_folder.png differ
diff --git a/assets/jenkins_is_ready.png b/assets/jenkins_is_ready.png
new file mode 100644
index 0000000..53aec90
Binary files /dev/null and b/assets/jenkins_is_ready.png differ
diff --git a/assets/jenkins_plugin_install.png b/assets/jenkins_plugin_install.png
new file mode 100644
index 0000000..ae314b1
Binary files /dev/null and b/assets/jenkins_plugin_install.png differ
diff --git a/assets/jenkins_restart_check.png b/assets/jenkins_restart_check.png
new file mode 100644
index 0000000..700c2b2
Binary files /dev/null and b/assets/jenkins_restart_check.png differ
diff --git a/assets/jenkins_restarting.png b/assets/jenkins_restarting.png
new file mode 100644
index 0000000..551267b
Binary files /dev/null and b/assets/jenkins_restarting.png differ
diff --git a/assets/jenkins_stage_view.png b/assets/jenkins_stage_view.png
new file mode 100644
index 0000000..ecf2811
Binary files /dev/null and b/assets/jenkins_stage_view.png differ
diff --git a/assets/jenkins_status.png b/assets/jenkins_status.png
new file mode 100644
index 0000000..357239c
Binary files /dev/null and b/assets/jenkins_status.png differ
diff --git a/assets/jenkins_suggested_plugin.png b/assets/jenkins_suggested_plugin.png
new file mode 100644
index 0000000..91277ea
Binary files /dev/null and b/assets/jenkins_suggested_plugin.png differ
diff --git a/assets/jenkins_suggested_plugin2.png b/assets/jenkins_suggested_plugin2.png
new file mode 100644
index 0000000..bacd40b
Binary files /dev/null and b/assets/jenkins_suggested_plugin2.png differ
diff --git a/assets/jenkins_tag.png b/assets/jenkins_tag.png
new file mode 100644
index 0000000..7859a50
Binary files /dev/null and b/assets/jenkins_tag.png differ
diff --git a/assets/jenkins_tag_build.png b/assets/jenkins_tag_build.png
new file mode 100644
index 0000000..4c06153
Binary files /dev/null and b/assets/jenkins_tag_build.png differ
diff --git a/assets/jenkins_user.png b/assets/jenkins_user.png
new file mode 100644
index 0000000..257cfc1
Binary files /dev/null and b/assets/jenkins_user.png differ
diff --git a/assets/jpa_compare_1.png b/assets/jpa_compare_1.png
new file mode 100644
index 0000000..111ef31
Binary files /dev/null and b/assets/jpa_compare_1.png differ
diff --git a/assets/json_format.png b/assets/json_format.png
new file mode 100644
index 0000000..215f041
Binary files /dev/null and b/assets/json_format.png differ
diff --git a/assets/k3s_cluster_added.png b/assets/k3s_cluster_added.png
new file mode 100644
index 0000000..b14bc0b
Binary files /dev/null and b/assets/k3s_cluster_added.png differ
diff --git a/assets/k3s_config_modify.png b/assets/k3s_config_modify.png
new file mode 100644
index 0000000..3fba3b0
Binary files /dev/null and b/assets/k3s_config_modify.png differ
diff --git a/assets/k3s_disk.png b/assets/k3s_disk.png
new file mode 100644
index 0000000..45c453a
Binary files /dev/null and b/assets/k3s_disk.png differ
diff --git a/assets/k3s_kube_config.png b/assets/k3s_kube_config.png
new file mode 100644
index 0000000..ad7602b
Binary files /dev/null and b/assets/k3s_kube_config.png differ
diff --git a/assets/k3s_nodes.png b/assets/k3s_nodes.png
new file mode 100644
index 0000000..f2634a3
Binary files /dev/null and b/assets/k3s_nodes.png differ
diff --git a/assets/k3s_reload.png b/assets/k3s_reload.png
new file mode 100644
index 0000000..5f17022
Binary files /dev/null and b/assets/k3s_reload.png differ
diff --git a/assets/k3s_status.png b/assets/k3s_status.png
new file mode 100644
index 0000000..a3d56da
Binary files /dev/null and b/assets/k3s_status.png differ
diff --git a/assets/k8s_component.png b/assets/k8s_component.png
new file mode 100644
index 0000000..81922b9
Binary files /dev/null and b/assets/k8s_component.png differ
diff --git a/assets/k8s_deployment.png b/assets/k8s_deployment.png
new file mode 100644
index 0000000..d64173c
Binary files /dev/null and b/assets/k8s_deployment.png differ
diff --git a/assets/k8s_describe_role.png b/assets/k8s_describe_role.png
new file mode 100644
index 0000000..d6727f4
Binary files /dev/null and b/assets/k8s_describe_role.png differ
diff --git a/assets/k8s_describe_sa.png b/assets/k8s_describe_sa.png
new file mode 100644
index 0000000..249ffdb
Binary files /dev/null and b/assets/k8s_describe_sa.png differ
diff --git a/assets/k8s_describe_secret_sa.png b/assets/k8s_describe_secret_sa.png
new file mode 100644
index 0000000..06a307a
Binary files /dev/null and b/assets/k8s_describe_secret_sa.png differ
diff --git a/assets/k8s_flow.png b/assets/k8s_flow.png
new file mode 100644
index 0000000..184e6ea
Binary files /dev/null and b/assets/k8s_flow.png differ
diff --git a/assets/k8s_flow2.png b/assets/k8s_flow2.png
new file mode 100644
index 0000000..a239f5a
Binary files /dev/null and b/assets/k8s_flow2.png differ
diff --git a/assets/k8s_flow3.png b/assets/k8s_flow3.png
new file mode 100644
index 0000000..bfc9b73
Binary files /dev/null and b/assets/k8s_flow3.png differ
diff --git a/assets/k8s_journey.png b/assets/k8s_journey.png
new file mode 100644
index 0000000..0d7654a
Binary files /dev/null and b/assets/k8s_journey.png differ
diff --git a/assets/k8s_overview_1.png b/assets/k8s_overview_1.png
new file mode 100644
index 0000000..e0a9347
Binary files /dev/null and b/assets/k8s_overview_1.png differ
diff --git a/assets/k8s_overview_10.png b/assets/k8s_overview_10.png
new file mode 100644
index 0000000..0b09a96
Binary files /dev/null and b/assets/k8s_overview_10.png differ
diff --git a/assets/k8s_overview_2.png b/assets/k8s_overview_2.png
new file mode 100644
index 0000000..a09591e
Binary files /dev/null and b/assets/k8s_overview_2.png differ
diff --git a/assets/k8s_overview_3.png b/assets/k8s_overview_3.png
new file mode 100644
index 0000000..a534c01
Binary files /dev/null and b/assets/k8s_overview_3.png differ
diff --git a/assets/k8s_overview_4.png b/assets/k8s_overview_4.png
new file mode 100644
index 0000000..5a881f5
Binary files /dev/null and b/assets/k8s_overview_4.png differ
diff --git a/assets/k8s_overview_5.png b/assets/k8s_overview_5.png
new file mode 100644
index 0000000..32418fc
Binary files /dev/null and b/assets/k8s_overview_5.png differ
diff --git a/assets/k8s_overview_6.png b/assets/k8s_overview_6.png
new file mode 100644
index 0000000..6d431dd
Binary files /dev/null and b/assets/k8s_overview_6.png differ
diff --git a/assets/k8s_overview_7.png b/assets/k8s_overview_7.png
new file mode 100644
index 0000000..93e9b9b
Binary files /dev/null and b/assets/k8s_overview_7.png differ
diff --git a/assets/k8s_overview_8.png b/assets/k8s_overview_8.png
new file mode 100644
index 0000000..bb91e1e
Binary files /dev/null and b/assets/k8s_overview_8.png differ
diff --git a/assets/k8s_overview_9.png b/assets/k8s_overview_9.png
new file mode 100644
index 0000000..49e612c
Binary files /dev/null and b/assets/k8s_overview_9.png differ
diff --git a/assets/kt_cloud_vm_create.png b/assets/kt_cloud_vm_create.png
new file mode 100644
index 0000000..32feb68
Binary files /dev/null and b/assets/kt_cloud_vm_create.png differ
diff --git a/assets/kubeconfig_paste.png b/assets/kubeconfig_paste.png
new file mode 100644
index 0000000..0993d88
Binary files /dev/null and b/assets/kubeconfig_paste.png differ
diff --git a/assets/kubectl_download.png b/assets/kubectl_download.png
new file mode 100644
index 0000000..f3f42fc
Binary files /dev/null and b/assets/kubectl_download.png differ
diff --git a/assets/kubectl_stable.png b/assets/kubectl_stable.png
new file mode 100644
index 0000000..933304a
Binary files /dev/null and b/assets/kubectl_stable.png differ
diff --git a/assets/kubectl_version.png b/assets/kubectl_version.png
new file mode 100644
index 0000000..111b537
Binary files /dev/null and b/assets/kubectl_version.png differ
diff --git a/assets/kustomize_argocd_dev1.png b/assets/kustomize_argocd_dev1.png
new file mode 100644
index 0000000..2cdc042
Binary files /dev/null and b/assets/kustomize_argocd_dev1.png differ
diff --git a/assets/kustomize_argocd_dev2.png b/assets/kustomize_argocd_dev2.png
new file mode 100644
index 0000000..0cce439
Binary files /dev/null and b/assets/kustomize_argocd_dev2.png differ
diff --git a/assets/kustomize_argocd_dev3.png b/assets/kustomize_argocd_dev3.png
new file mode 100644
index 0000000..1f26908
Binary files /dev/null and b/assets/kustomize_argocd_dev3.png differ
diff --git a/assets/kustomize_base.png b/assets/kustomize_base.png
new file mode 100644
index 0000000..03f72d2
Binary files /dev/null and b/assets/kustomize_base.png differ
diff --git a/assets/kustomize_config.png b/assets/kustomize_config.png
new file mode 100644
index 0000000..16bd723
Binary files /dev/null and b/assets/kustomize_config.png differ
diff --git a/assets/kustomize_overlay.png b/assets/kustomize_overlay.png
new file mode 100644
index 0000000..d4ba55a
Binary files /dev/null and b/assets/kustomize_overlay.png differ
diff --git a/assets/lens_cluster.png b/assets/lens_cluster.png
new file mode 100644
index 0000000..44a8130
Binary files /dev/null and b/assets/lens_cluster.png differ
diff --git a/assets/lens_cluster_list.png b/assets/lens_cluster_list.png
new file mode 100644
index 0000000..84f36aa
Binary files /dev/null and b/assets/lens_cluster_list.png differ
diff --git a/assets/lens_config.png b/assets/lens_config.png
new file mode 100644
index 0000000..ed4f53f
Binary files /dev/null and b/assets/lens_config.png differ
diff --git a/assets/lens_first_screeen.png b/assets/lens_first_screeen.png
new file mode 100644
index 0000000..baac6d5
Binary files /dev/null and b/assets/lens_first_screeen.png differ
diff --git a/assets/lens_metric_install.png b/assets/lens_metric_install.png
new file mode 100644
index 0000000..74cf2d3
Binary files /dev/null and b/assets/lens_metric_install.png differ
diff --git a/assets/lens_preview.png b/assets/lens_preview.png
new file mode 100644
index 0000000..5e21c18
Binary files /dev/null and b/assets/lens_preview.png differ
diff --git a/assets/lens_web.png b/assets/lens_web.png
new file mode 100644
index 0000000..a73b76c
Binary files /dev/null and b/assets/lens_web.png differ
diff --git a/assets/lens_welcome.png b/assets/lens_welcome.png
new file mode 100644
index 0000000..528fdf7
Binary files /dev/null and b/assets/lens_welcome.png differ
diff --git a/assets/lens_welcome_0.png b/assets/lens_welcome_0.png
new file mode 100644
index 0000000..228a2ce
Binary files /dev/null and b/assets/lens_welcome_0.png differ
diff --git a/assets/manage_jenkins1.png b/assets/manage_jenkins1.png
new file mode 100644
index 0000000..1ef3e94
Binary files /dev/null and b/assets/manage_jenkins1.png differ
diff --git a/assets/maven_user_setting.png b/assets/maven_user_setting.png
new file mode 100644
index 0000000..72f3ff6
Binary files /dev/null and b/assets/maven_user_setting.png differ
diff --git a/assets/model1.png b/assets/model1.png
new file mode 100644
index 0000000..ab29d21
Binary files /dev/null and b/assets/model1.png differ
diff --git a/assets/model2.png b/assets/model2.png
new file mode 100644
index 0000000..a400604
Binary files /dev/null and b/assets/model2.png differ
diff --git a/assets/model3.png b/assets/model3.png
new file mode 100644
index 0000000..5f5ab13
Binary files /dev/null and b/assets/model3.png differ
diff --git a/assets/modify1.png b/assets/modify1.png
new file mode 100644
index 0000000..c13fd04
Binary files /dev/null and b/assets/modify1.png differ
diff --git a/assets/modify2.png b/assets/modify2.png
new file mode 100644
index 0000000..2ef5923
Binary files /dev/null and b/assets/modify2.png differ
diff --git a/assets/modify3.png b/assets/modify3.png
new file mode 100644
index 0000000..5baba4b
Binary files /dev/null and b/assets/modify3.png differ
diff --git a/assets/modify4.png b/assets/modify4.png
new file mode 100644
index 0000000..5d8784c
Binary files /dev/null and b/assets/modify4.png differ
diff --git a/assets/modify5.png b/assets/modify5.png
new file mode 100644
index 0000000..f59814e
Binary files /dev/null and b/assets/modify5.png differ
diff --git a/assets/modify6.png b/assets/modify6.png
new file mode 100644
index 0000000..4e7c678
Binary files /dev/null and b/assets/modify6.png differ
diff --git a/assets/modify7.png b/assets/modify7.png
new file mode 100644
index 0000000..e7d80db
Binary files /dev/null and b/assets/modify7.png differ
diff --git a/assets/modify8.png b/assets/modify8.png
new file mode 100644
index 0000000..cac1995
Binary files /dev/null and b/assets/modify8.png differ
diff --git a/assets/modify9.png b/assets/modify9.png
new file mode 100644
index 0000000..545d77e
Binary files /dev/null and b/assets/modify9.png differ
diff --git a/assets/mvc1.png b/assets/mvc1.png
new file mode 100644
index 0000000..70144c7
Binary files /dev/null and b/assets/mvc1.png differ
diff --git a/assets/mvc2.png b/assets/mvc2.png
new file mode 100644
index 0000000..5a0338b
Binary files /dev/null and b/assets/mvc2.png differ
diff --git a/assets/mvc3.png b/assets/mvc3.png
new file mode 100644
index 0000000..910f57a
Binary files /dev/null and b/assets/mvc3.png differ
diff --git a/assets/mvc4.png b/assets/mvc4.png
new file mode 100644
index 0000000..972c701
Binary files /dev/null and b/assets/mvc4.png differ
diff --git a/assets/mvc5.png b/assets/mvc5.png
new file mode 100644
index 0000000..f566fe5
Binary files /dev/null and b/assets/mvc5.png differ
diff --git a/assets/mvc6.png b/assets/mvc6.png
new file mode 100644
index 0000000..c097975
Binary files /dev/null and b/assets/mvc6.png differ
diff --git a/assets/mvc7.png b/assets/mvc7.png
new file mode 100644
index 0000000..922359e
Binary files /dev/null and b/assets/mvc7.png differ
diff --git a/assets/mvc8.png b/assets/mvc8.png
new file mode 100644
index 0000000..409401e
Binary files /dev/null and b/assets/mvc8.png differ
diff --git a/assets/mvc9.png b/assets/mvc9.png
new file mode 100644
index 0000000..0442bcf
Binary files /dev/null and b/assets/mvc9.png differ
diff --git a/assets/new_namespace.png b/assets/new_namespace.png
new file mode 100644
index 0000000..05c910f
Binary files /dev/null and b/assets/new_namespace.png differ
diff --git a/assets/node.png b/assets/node.png
new file mode 100644
index 0000000..035d0e5
Binary files /dev/null and b/assets/node.png differ
diff --git a/assets/nodeport_test.png b/assets/nodeport_test.png
new file mode 100644
index 0000000..facf166
Binary files /dev/null and b/assets/nodeport_test.png differ
diff --git a/assets/nodeport_traffic.png b/assets/nodeport_traffic.png
new file mode 100644
index 0000000..234da72
Binary files /dev/null and b/assets/nodeport_traffic.png differ
diff --git a/assets/okd_cluster_info_detail.png b/assets/okd_cluster_info_detail.png
new file mode 100644
index 0000000..98eaca2
Binary files /dev/null and b/assets/okd_cluster_info_detail.png differ
diff --git a/assets/okd_custer_info.png b/assets/okd_custer_info.png
new file mode 100644
index 0000000..4b321aa
Binary files /dev/null and b/assets/okd_custer_info.png differ
diff --git a/assets/openshift_account.png b/assets/openshift_account.png
new file mode 100644
index 0000000..c134dfe
Binary files /dev/null and b/assets/openshift_account.png differ
diff --git a/assets/pipeline_credential.png b/assets/pipeline_credential.png
new file mode 100644
index 0000000..4565bc5
Binary files /dev/null and b/assets/pipeline_credential.png differ
diff --git a/assets/pipeline_git.png b/assets/pipeline_git.png
new file mode 100644
index 0000000..82590af
Binary files /dev/null and b/assets/pipeline_git.png differ
diff --git a/assets/pipeline_git2.png b/assets/pipeline_git2.png
new file mode 100644
index 0000000..11066fa
Binary files /dev/null and b/assets/pipeline_git2.png differ
diff --git a/assets/pipeline_log_rotation.png b/assets/pipeline_log_rotation.png
new file mode 100644
index 0000000..f5965d2
Binary files /dev/null and b/assets/pipeline_log_rotation.png differ
diff --git a/assets/pipeline_main.png b/assets/pipeline_main.png
new file mode 100644
index 0000000..7e0e766
Binary files /dev/null and b/assets/pipeline_main.png differ
diff --git a/assets/pipeline_newitem.png b/assets/pipeline_newitem.png
new file mode 100644
index 0000000..4f242cc
Binary files /dev/null and b/assets/pipeline_newitem.png differ
diff --git a/assets/pipeline_scriptpath.png b/assets/pipeline_scriptpath.png
new file mode 100644
index 0000000..873cf24
Binary files /dev/null and b/assets/pipeline_scriptpath.png differ
diff --git a/assets/plugin_git.png b/assets/plugin_git.png
new file mode 100644
index 0000000..fb41bb0
Binary files /dev/null and b/assets/plugin_git.png differ
diff --git a/assets/pod.png b/assets/pod.png
new file mode 100644
index 0000000..dd38a27
Binary files /dev/null and b/assets/pod.png differ
diff --git a/assets/port_forwarding1.png b/assets/port_forwarding1.png
new file mode 100644
index 0000000..1fb034f
Binary files /dev/null and b/assets/port_forwarding1.png differ
diff --git a/assets/port_forwarding2.png b/assets/port_forwarding2.png
new file mode 100644
index 0000000..7d6775d
Binary files /dev/null and b/assets/port_forwarding2.png differ
diff --git a/assets/portainer_admin.png b/assets/portainer_admin.png
new file mode 100644
index 0000000..0c3bf60
Binary files /dev/null and b/assets/portainer_admin.png differ
diff --git a/assets/portainer_container.png b/assets/portainer_container.png
new file mode 100644
index 0000000..f8df15d
Binary files /dev/null and b/assets/portainer_container.png differ
diff --git a/assets/portainer_dashboard.png b/assets/portainer_dashboard.png
new file mode 100644
index 0000000..29296e9
Binary files /dev/null and b/assets/portainer_dashboard.png differ
diff --git a/assets/portainer_docker_volume.png b/assets/portainer_docker_volume.png
new file mode 100644
index 0000000..86af205
Binary files /dev/null and b/assets/portainer_docker_volume.png differ
diff --git a/assets/portainer_local.png b/assets/portainer_local.png
new file mode 100644
index 0000000..cb1d26d
Binary files /dev/null and b/assets/portainer_local.png differ
diff --git a/assets/private_ip_create.png b/assets/private_ip_create.png
new file mode 100644
index 0000000..d65b7f0
Binary files /dev/null and b/assets/private_ip_create.png differ
diff --git a/assets/private_ip_info.png b/assets/private_ip_info.png
new file mode 100644
index 0000000..4499e13
Binary files /dev/null and b/assets/private_ip_info.png differ
diff --git a/assets/private_ip_modify.png b/assets/private_ip_modify.png
new file mode 100644
index 0000000..c1d786f
Binary files /dev/null and b/assets/private_ip_modify.png differ
diff --git a/assets/private_ip_more.png b/assets/private_ip_more.png
new file mode 100644
index 0000000..fdab0d7
Binary files /dev/null and b/assets/private_ip_more.png differ
diff --git a/assets/project_overview.png b/assets/project_overview.png
new file mode 100644
index 0000000..057a087
Binary files /dev/null and b/assets/project_overview.png differ
diff --git a/assets/repository_create.png b/assets/repository_create.png
new file mode 100644
index 0000000..9c6356c
Binary files /dev/null and b/assets/repository_create.png differ
diff --git a/assets/role_rolebinding.png b/assets/role_rolebinding.png
new file mode 100644
index 0000000..aa8b448
Binary files /dev/null and b/assets/role_rolebinding.png differ
diff --git a/assets/root_du_h.png b/assets/root_du_h.png
new file mode 100644
index 0000000..bd585e5
Binary files /dev/null and b/assets/root_du_h.png differ
diff --git a/assets/scale_out_after.png b/assets/scale_out_after.png
new file mode 100644
index 0000000..c56e7e4
Binary files /dev/null and b/assets/scale_out_after.png differ
diff --git a/assets/scale_out_before.png b/assets/scale_out_before.png
new file mode 100644
index 0000000..69c7e84
Binary files /dev/null and b/assets/scale_out_before.png differ
diff --git a/assets/service.png b/assets/service.png
new file mode 100644
index 0000000..50a90c1
Binary files /dev/null and b/assets/service.png differ
diff --git a/assets/service_type.png b/assets/service_type.png
new file mode 100644
index 0000000..e02e060
Binary files /dev/null and b/assets/service_type.png differ
diff --git a/assets/shclub_edu_file.png b/assets/shclub_edu_file.png
new file mode 100644
index 0000000..f7924f3
Binary files /dev/null and b/assets/shclub_edu_file.png differ
diff --git a/assets/swagger1.png b/assets/swagger1.png
new file mode 100644
index 0000000..7480262
Binary files /dev/null and b/assets/swagger1.png differ
diff --git a/assets/swagger2.png b/assets/swagger2.png
new file mode 100644
index 0000000..ebdb58e
Binary files /dev/null and b/assets/swagger2.png differ
diff --git a/assets/swagger3.png b/assets/swagger3.png
new file mode 100644
index 0000000..a1e1bc6
Binary files /dev/null and b/assets/swagger3.png differ
diff --git a/assets/swagger4.png b/assets/swagger4.png
new file mode 100644
index 0000000..9162cea
Binary files /dev/null and b/assets/swagger4.png differ
diff --git a/assets/swagger_first.png b/assets/swagger_first.png
new file mode 100644
index 0000000..671f88f
Binary files /dev/null and b/assets/swagger_first.png differ
diff --git a/assets/swagger_second.png b/assets/swagger_second.png
new file mode 100644
index 0000000..712ef16
Binary files /dev/null and b/assets/swagger_second.png differ
diff --git a/assets/swagger_third.png b/assets/swagger_third.png
new file mode 100644
index 0000000..8938097
Binary files /dev/null and b/assets/swagger_third.png differ
diff --git a/assets/swagger_tool.png b/assets/swagger_tool.png
new file mode 100644
index 0000000..6d5359e
Binary files /dev/null and b/assets/swagger_tool.png differ
diff --git a/assets/switch_context.png b/assets/switch_context.png
new file mode 100644
index 0000000..78822dc
Binary files /dev/null and b/assets/switch_context.png differ
diff --git a/assets/top_nodes.png b/assets/top_nodes.png
new file mode 100644
index 0000000..3342cec
Binary files /dev/null and b/assets/top_nodes.png differ
diff --git a/assets/update_app_dockerhub_v2.png b/assets/update_app_dockerhub_v2.png
new file mode 100644
index 0000000..fe8554a
Binary files /dev/null and b/assets/update_app_dockerhub_v2.png differ
diff --git a/assets/update_app_jenkins2.png b/assets/update_app_jenkins2.png
new file mode 100644
index 0000000..0d364a0
Binary files /dev/null and b/assets/update_app_jenkins2.png differ
diff --git a/assets/update_app_v2.png b/assets/update_app_v2.png
new file mode 100644
index 0000000..12ccec5
Binary files /dev/null and b/assets/update_app_v2.png differ
diff --git a/assets/vi_dockerfile.png b/assets/vi_dockerfile.png
new file mode 100644
index 0000000..d359a84
Binary files /dev/null and b/assets/vi_dockerfile.png differ
diff --git a/assets/vi_k3s_service.png b/assets/vi_k3s_service.png
new file mode 100644
index 0000000..5200e32
Binary files /dev/null and b/assets/vi_k3s_service.png differ
diff --git a/assets/vi_tls_san.png b/assets/vi_tls_san.png
new file mode 100644
index 0000000..8396978
Binary files /dev/null and b/assets/vi_tls_san.png differ
diff --git a/assets/vm_created.png b/assets/vm_created.png
new file mode 100644
index 0000000..aa2eb91
Binary files /dev/null and b/assets/vm_created.png differ
diff --git a/assets/web_ingress.png b/assets/web_ingress.png
new file mode 100644
index 0000000..ddff15a
Binary files /dev/null and b/assets/web_ingress.png differ
diff --git a/assets/youtube_daily.png b/assets/youtube_daily.png
new file mode 100644
index 0000000..e5d226d
Binary files /dev/null and b/assets/youtube_daily.png differ
diff --git a/chapter1.md b/chapter1.md
new file mode 100644
index 0000000..d7e3ab0
--- /dev/null
+++ b/chapter1.md
@@ -0,0 +1,800 @@
+
+# Chapter 1
+
+Docker에 대해서 자세한 설명과 함께 Jenkins CI 구성 요소인 Git에 대한 실습을 한다.
+
+1. Docker
+
+2. Dockerfile 구성 및 빌드 , Tag , Push 그리고 Swagger 실습
+
+3. Jenkins를 사용한 CI 구성 하기
+
+
+## Docker
+
+
+
+### Docker 란 ?
+
+
+
+소개 및 배경
+- 도커는 컨테이너 기반의 오픈소스 가상화 플랫폼이다.
+- 다양한 이유로 계속 바뀌는 서버 환경과 개발 환경 문제를 해결하기 위해 등장했다.
+ (툴 업데이트, 회사의 툴 사용 변경, 회사의 언어 정책 변경 등 )
+- 기존에 서버와 개발 환경이 변경되면 컴퓨터 셋팅(개발 환경) 등을 다시하거나
+ 그 과정에서 발생하는 문제들과 같이, 여러모로 불편한 점이 많았다.
+- 도커가 등장하고 서버관리/개발 방식이(컨테이너) 완전히 편리하게 바뀌게 된다.
+
+사용하는 이유
+- 도커 허브에 올라온 이미지와 docker-compose.yml의 설정으로 원하는
+ 프로그램을 편안하게 설치가 가능하다.
+- 컨테이너를 생성하여 분리된 환경에 설치하므로 제거도 쉽다.
+- 하나의 서버(로컬 호스트)에 포트만 변경하여 동일한 프로그램을 실행하기도 쉽다.
+- 도커를 사용하지 않으면 환경변수나 경로 등의 충돌로 피곤하기 그지 없다.
+
+서버관리 방식의 변화
+- 전통적인 서버관리 방식은 아래와 같이 각 단계별로 흐름이 있었고, 각 단계가
+ 업데이트 되거나 어떤 문제가 발생하면 전체 흐름이 중단되는 문제가 있었다.
+- 도커 등장 이후 어떠한 프로그램도 컨테이너로 만들 수 있다.
+- 서로 다른 프로그램이더라도 컨테이너로 규격화 되었음
+- AWS, Azure, Google cloud 등 어떤 환경에서도 돌아간다.
+
+- 가상머신과 비슷하게 생각할 수 있지만 비슷한점과 다른점이 있다.
+- 가상머신처럼 독립적으로 실행되지만, 가상머신보다 빠르고 쉽고 효율적이다.
+- 도커는 컴퓨터 자원을 그대로 사용한다.
+
+도커의 특징
+- 도커는 가상머신이 아니고 격리만 해주기 떄문에 성능상 하락이 없다. (성능 하락이 큰 VM과 다르다.)
+- 확장성과 이식성
+ - 도커가 설치되어 있다면 어디서든 컨테이너를 실행할 수 있다.
+ - 오픈 소스이기에 특정 회사나 서비스에 종속적이지 않다.
+ - 쉽게 개발서버를 만들 수 있고 테스트 서버 생성도 가능하다.
+- 표준성
+ - 도커를 사용하지 않는 경우, 각각의 언어로 만든 서비스들의 배포 방식은 모두 다르다.
+ - 도커는 컨테이너라는 표준으로 서버를 배포하므로 모든 서비스들의 배포 과정이 동일해진다.
+- 이미지
+ - 컨테이너를 실행하기 위한 압축파일과 같은 개념이다.
+ - 이미지에서 컨테이너를 생성하기 떄문에 반드시 이미지를 만드는 과정이 필요하다.
+ - Dockerfile을 이용하여 이미지를 만들고 처음부터 재현 가능하다.
+ - 빌드 서버에서 이미지를 만들면 해당 이미지를 이미지 저장소(허브)에 저장하고 운영서버에서 이미지를 불러와 사용한다.
+- 설정관리
+ - 도커에서 설정은 보통 아래와 같이 환경변수로 제어한다.
+ - MYSQL_PASS=password와 같이 컨테이너를 띄울 때 환경변수를 같이 지정한다.
+ - 하나의 이미지가 환경변수에 따라 동적으로 설정파일을 생성하도록 만들어져야한다.
+- 자원관리
+ - 컨테이너는 삭제 후 새로 만들면 모든 데이터가 초기화된다. (제거가 쉽다.)
+ - 그러므로 저장이 필요하다면, 업로드 파일을 외부 스토리지와 링크하여 사용하거나 S3같은 별도의 저장소가 필요하다.
+ - 세션이나 캐시를 memcached나 redis와 같은 외부로 분리한다.
+
+
+
+Github 소스를 다운 로드 하기 위하여 Git을 설치 해야 한다.
+
+
+
+## Git
+
+### Git 개요
+
+Git이란 소스코드를 효과적으로 관리하기 위해 개발된 '분산형 버전 관리 시스템'입니다. ( 이전 에는 SVN 많이 사용 )
+
+가장 대중적인 SaaS 형태는 Microsoft에서 제공하는 GitHub 이고
+Private 형태로는 Gitlab을 많이 사용 함.
+
+
+
+참고 사이트 : https://backlog.com/git-tutorial/kr/intro/intro1_1.html
+
+
+
+### Git 설치.
+
+
+
+본인의 PC에 git을 설치하고 버전을 확인한다.
+
+```bash
+git --version
+```
+
+
+
+
+
+### Git Clone 하여 github 소스 가져오기.
+
+github에서 shclub/edu2 를 선택하고 code를 클릭한다.
+https의 url를 복사한다. 오른쪽 복사 아이콘 클릭
+
+
+
+터미털에서 git clone 명령어를 사용하여 로컬에 소스를 가져온다.
+정상적으로 가져오면 edu2 폴더로 이동하여 파일 받아진것 확인한다.
+
+```bash
+git clone https://github.com/shclub/edu2.git
+```
+
+
+
+
+
+
+
+### Dockerfile
+
+
+
+도커 이미지를 만들기 위해서는 Dockerfile 을 생성 해야 한다.
+
+Dockerfile 예제
+
+```yaml
+
+# 베이스 이미지 이며 이미지 이름 앞에 아무것도 없으면 docker hub에서 가져온다.
+FROM python:3.8-slim
+
+# 컨테이너 안에 작업 폴더를 설정한다.
+WORKDIR /app
+
+# 로컬 현대 폴더의 모든 값을 컨테이너 /app 폴더에 복사한다.
+ADD . /app
+
+# pyhon의 경우 library를 requirements.txt에 기술을 하였고 RUN 명령어를 # 사용하여 아래 구문을 실행한다.
+
+# RUN pip install -r requirements.txt
+
+# 직접 라이브러리를 추가 할수 있다. 단 라이브러리가 많아지면 불편하다.
+RUN pip3 install flask==2.0.3
+RUN pip3 install flask-cors==3.0.10
+RUN pip3 install flask_restx
+RUN pip3 install Werkzeug==2.0.3
+
+# 기본 이미지는 대부분 GMT+0 기준으로 생성되어 한국 시간으로 변경 해준다
+RUN ln -snf /usr/share/zoneinfo/Asia/Seoul /etc/localtime && echo Asia/Seoul > /etc/timezone
+
+# 컨테이너 외부에서 접속할 포트를 적어준다.
+EXPOSE 40003
+
+# 컨테이너 로딩시에 사용하는 명령어를 기입한다.
+# EntryPoint와 비슷 하나 CMD를 사용하면 docker run시에 파라미터 설정 가능하다.
+CMD ["python", "app.py"]
+```
+
+터미널에서 edu2 폴더로 이동하여 vi 에디터로 Dockerfile를 생성한다.
+
+```bash
+vi Dockerfile
+```
+
+
+
+
+
+i (소문자) 를 누른 후 위의 Dockerfile 내용을 복사하여 붙여넣기 한다.
+esc 키를 누른 후 :wq를 입력하여 저장하고 나온다.
+
+
+그리고 app.py 화일을 아래 소스를 사용하여 생성한다.
+
+```bash
+vi app.py
+```
+
+app.py
+
+
+```python
+# -*- coding:utf-8 -*-
+# REST API로 구현한 계산기 예제
+
+import werkzeug
+werkzeug.cached_property = werkzeug.utils.cached_property
+
+from flask import Flask
+from flask_restx import Resource, Api, reqparse
+
+
+# -----------------------------------------------------
+# api
+# -----------------------------------------------------
+app = Flask(__name__)
+api = Api(app, version='1.0', title='Calc API',
+ description='계산기 REST API 문서',)
+
+ns = api.namespace('calc', description='계산기 API 목록')
+app.config.SWAGGER_UI_DOC_EXPANSION = 'list' # None, list, full
+
+
+# -----------------------------------------------------
+# 덧셈을 위한 API 정의
+# -----------------------------------------------------
+sum_parser = ns.parser()
+sum_parser.add_argument('value1', required=True, help='연산자1')
+sum_parser.add_argument('value2', required=True, help='연산자2')
+
+
+@ns.route('/sum')
+@ns.expect(sum_parser)
+class FileReport(Resource):
+ def get(self):
+ """
+ Calculate addition
+ """
+ args = sum_parser.parse_args()
+
+ try:
+ val1 = args['value1']
+ val2 = args['value2']
+ except KeyError:
+ return {'result': 'ERROR_PARAMETER'}, 500
+
+ result = {'result': 'ERROR_SUCCESS','value': int(val1) + int(val2)}
+ return result, 200
+
+
+if __name__ == '__main__':
+ app.run(host='0.0.0.0', port=5000) # , debug=True)
+```
+
+
+아래 명령어를 사용하여 도커 파일을 생성한다.
+Dockerfile 위치와 같은 폴더에서 실행하여야 하며 생성할 이미지 이름 뒤에 . 을 반드시 사용한다.
+
+```bash
+docker build -t edu2 .
+```
+
+
+
+Dockerfile의 line by line 으로 단계가 구성되어 도커 레이어를 생성을 한다.
+내부 적으로는 docker commit 명령어가 실행이 되면 도커를 재 생성시에는
+Cache를 사용 하기 때문에 훨씬 빨리 빌드가 된다.
+
+
+
+중간에 임시에 생성된 intermediate 컨테이너가 삭제가 되고 빌드 이미지가 만들어진다.
+
+아래 명령어를 사용하여 생성된 도커 이미지를 확인 할 수 있다.
+
+```bash
+docker images
+```
+
+
+
+Docker Hub에 전송하기 위해서는 tagging을 하고 push를 한다.
+tag 명령어 뒤에는 로컬 이미지 이름 , 다음에는 도커허브 이미지 이름을 입력.
+
+```bash
+docker tag edu2 (본인 도커 허브 ID)/edu2
+docker push (본인 도커 허브 ID)/edu2
+```
+
+
+
+Docker Hub에 페이지에서 push된 이미지를 확인한다.
+
+
+
+실행 중인 컨테이너를 확인하고 40003 포트를 사용하는 컨테이너를 stop 한다.
+```bash
+docker ps
+docker stop (컨테이너id)
+```
+
+
+
+도커 이미지를 실행한다.
+- -d : Detached 모드(백그라운드)
+- --name : 컨테이너에 이름을 부여한다.
+- -p : 포트 ( 외부접속포트 : 컨테이너 포트)
+- 맨 마지막에 도커 이미지 이름
+
+```bash
+docker run -d --name my-python -p 40003:5000 (본인 도커 허브 ID)/edu2
+```
+
+docker ps 명령어로 정상적인지 확인한다.
+
+
+
+docker ps 로 아무 것도 없으면 docker ps -a 명령어도 kill 된 컨테이너를 확인하고
+docker logs 명령어로 로그를 확인한다.
+
+
+```bash
+docker logs (컨테이너id)
+```
+
+
+에러가 발생한 도커는 같은 이름으로 docker run 명령어를 실행 하면 에러가 발생하기 때문에 아래 명령어를 실행 후에 위의 docker run 명령어를 다시 실행한다.
+
+```bash
+docker rm (컨테이너 이름)
+```
+
+docker 컨테이너 안으로 들어가 본다.
+
+```bash
+docker exec -it (컨테이너id) /bin/sh
+```
+
+컨테이너 내부에서 ls 명령어를 쳐보면 도커 빌드시 복사되었던 소스를 확인 할 수 있다.
+
+
+
+exit 명령어를 사용하여 컨테이너 내부에서 나올수 있다.
+
+현재 컨테이너의 내용을 도커 이미지로 저장하고 싶을때는 commit 명령어를 사용한다.
+- -m : 뒤에 comment를 적어준다
+- 컨테이너 이름 : 현재 실행되는 컨테이너 이름 또는 컨테이너 아이디를 적여준다
+- 신규 도커 이미지 : 신규로 생성하고 싶은 로컬 도커이미지 이름을 적어준다
+
+```bash
+docker commit -m "new edu2" (컨테이너 이름) (생성하고싶은 이미지 이름):(버전)
+```
+
+
+
+docker stats 명령어를 사용하면 컨테이너별로 CPU/MEM을 볼 수 있다.
+
+```bash
+root@jakelee:~# docker stats
+```
+
+
+
+
+
+도커 추가 명령어
+- 정지 중인 컨테이너 삭제 : docker container prune
+- 이미지 , 정지되어 있는 컨테이너 , 네트웍크 삭제 : docker system prune
+- 도커 이미지 삭제 : docker rmi (도커 로컬 이미지 이름)
+- 컨테이너 삭제 : docker rm [CONTAINER_ID]
+- 컨테이너 일시정지 : docker stop [CONTAINER_ID]
+
+
+
+## GitHub 계정을 생성
+
+
+
+### https://github.com/ 접속하고 계정 생성
+
+
+
+### 계정 생성 후에 Repository를 생성한다.
+
+아래와 같이 이름 입력를 하고 README file check 를 한다.
+
+
+
+default 브랜치를 main에서 master로 변경한다. ( 맨 하단 setting 클릭하여 설정)
+
+
+
+
+
+### 교육용 repository인 https://github.com/shclub/edu1 를 fork 하여 본인의 Repository에 복사한다.
+
+총 4개 화일을 만들고 내용을 복사한다.
+
+
+
+ - 샘플은 pyhon flask 로 구성
+
+
+
+## Docker Hub 계정을 생성
+
+
+
+### https://hub.docker.com/ 접속하고 계정 생성 (향후 사내에서 개발시는 d-space Nexus 사용)
+
+
+
+### Docker 연동 테스트를 한다.
+
+```bash
+docker tag hello-world (본인id)/hello-world
+docker push (본인id)/hello-world
+```
+
+- 권한 에러 발생시 docker login 한다
+```bash
+docker login
+```
+
+
+
+정상적으로 로그인후 push를 한다.
+
+```bash
+docker push (본인id)/hello-world
+```
+
+
+
+도커허브 본인 계정에서 도커 이미지 생성 확인
+
+
+
+
+도커 이미지가 Private으로 되어 있으면 Public 으로 변경한다.
+- 개인 계정은 1개의 private 만 가능
+
+setting 으로 이동하여 Make public 클릭후 repository 이름을 입력후 Make Public 클릭
+
+
+
+
+
+## Jenkins 설정
+
+
+
+### 일반 사용자 계정을 생성한다
+
+웹 브라우저에서 http://211.252.85.148:9000/ 로 이동하여 로그인 한다.
+교육 계정은 기 생성 되어 있다. ( edu1 ~ edu25 )
+
+비밀번호는 계정 이름과 동일.
+
+
+
+
+Manage Jenkins -> Manage Users 로 이동한다. 사용자 생성 버튼 클릭 후 사용자 생성.
+
+
+
+
+
+### 계정 별 권한 부여방법
+
+
+
+Configure Global Security로 이동
+
+admin 계정으로 테스트 할 예정 으로 skip.
+
+
+
+
+생성한 계정을 입력하고 Add 클릭 추가후 권한 설정은 일단 Ovrall 체크 후 저장
+Project-based Matrix Authorization Strategy 체크 후 권한 설정
+
+
+
+
+
+### Github token 생성하기
+
+
+
+웹브라우저에서 Github 로그인하고
+Jenkins 에서 github repository 인증을 위해 사용할 token 을 생성한다.
+
+- Settings - Developer settings - Personal access tokens - Generate token
+선택해서 토큰 생성
+
+Github 사이트의 오른쪽 상단 본인 계정의 Setting으로 이동한다. ( 프로젝트의 setting이 아님 )
+
+
+
+
+
+Expiration 은 No Expiration으로 선택하고 repo, admin:repo_hook 만 체크하고 Generation Token 버튼 클릭해서 토큰 생성
+
+
+
+
+복사 아이콘을 클릭하여 토큰 값을 복사한다.
+- 다시 페이지에 들어가면 보이지 않아서 복사 후 저장 필요
+
+
+
+
+
+### GitHub Credential을 생성한다.
+
+
+
+Jenkins가 GitHub에서 Code를 가져올 수 있도록 Credential을 추가하자
+
+- Manage Jenkins -> Manage Credential -> System 으로 이동한다.
+
+
+
+Global Credential 클릭
+
+
+
+Add Credential를 클릭하면 계정 설정하는 화면이 나온다.
+
+
+
+Kind는 Username with password 를 선택해주시면 됩니다.
+
+Username 은 본인의 Github ID 를 선택해주시면 됩니다. ( 이메일 아님 )
+
+ID는 본인이 원하는 식별자를 넣어준다.
+
+password는 이전에 발급받은 Github Token 값을 입력한다.
+
+
+
+
+
+### Docker Hub Credential을 생성한다.
+
+
+
+Jenkins가 Docker Hub에 Image를 push 할 수 있도록 Credential을 추가하자
+
+- Manage Jenkins -> Manage Credential -> System -> Global Credential 로 이동한다.
+
+
+
+Add Credential를 클릭하면 계정 설정하는 화면이 나온다.
+
+Kind는 Username with password 를 선택해주시면 됩니다.
+
+Username 은 본인의 Docker Hub ID 를 선택해주시면 됩니다. ( 이메일 아님 )
+
+ID는 본인이 원하는 식별자를 넣어준다.
+
+password는 이전에 Docker Hub 본인 계정의 비밀번호 값을 입력한다.
+
+
+
+
+
+GitHub와 Docker Hub Credential 이 생선된 것을 확인한다.
+
+
+
+
+
+### 파이프 라인을 구성한다.
+
+
+
+메인 화면 좌측 메뉴에서 새로운 Item 선택
+
+
+
+item 이름을 입력하고 Pipeline 을 선택 후에 OK
+
+
+
+로그 Rotation을 5로 설정한다. 5개의 History 를 저장한다.
+
+
+
+Jenkins 홈 폴더로 이동하고 아래 명령어를 실행한다.
+
+- 홈 폴더 확인은 대쉬보드에서 Manage Jenkins -> Configure System으로 이동하여 보면 상단에 표시
+
+
+
+
+```bash
+cd /var/lib/jenkins
+ls ./jobs/edu1/builds
+```
+
+7개의 History중 3,4,5,6,7 5개만 저장된것을 확인 할 수있다.
+
+
+
+
+대쉬보드에서도 History를 확인 할 수 있다.
+
+
+
+
+
+Github Project URL을 설정하고 Git Parameter 를 체크하고 Parameter Type은 교육을 위한 용도 임으로 Branch를 선택한다. ( Tag는 Release를 위한 빌드 방식으로 그 당시 snapshot 이다. )
+
+Branch의 default는 orgin/master를 설정한다.
+
+
+
+
+
+* Tag 를 사용한 빌드는 맨 아래 부분을 참고한다.
+
+
+
+Build Triggers - GitHub hook trigger for GITScm polling 선택
+Repository 에 Git Url을 입력한다.
+Credential에 Jenkins에서 생성한 github_ci를 선택하여 추가한다.
+
+
+
+
+Script Path는 Jenkinsfile 로 설정한다.
+github에 대소문자 구문하여 Jenkinsfile 이 있어야함.
+Save 버튼을 클릭하여 저장한다.
+
+
+
+
+
+### 빌드 실행
+
+
+
+빌드 하기 전에 Jenkins 화일로 이동하여 docker hub 의 repository와 docker credential은 본인의 계정으로 설정한다.
+
+대쉬보드에서 Build With Parameter를 선택하고 Branch 선택 후 빌드 한다.
+
+
+
+빌드가 진행 되는 것을 단계별로 확인 할 수 있다.
+
+
+
+에러가 발생하면 해당 단계에서 마우스 오른쪽 버튼을 클릭하여 로그 확인 할수 있다.
+또한 왼쪽 하단의 Build History 에서 해당 빌드 번호를 클릭하여 자세한 에러를 볼수 있다.
+
+
+
+Console Output을 선택하고 에러를 확인 할 수 있다.
+
+
+
+아래 에러는 docker hub에서 repository가 private 으로 설정이 되어 발생한 에러이고 위에 설명한 대로 public 으로 변경하면 에러가 발생하지 않는다.
+
+
+
+대쉬보드에서 해당 파이프라인인 edu1을 선택하고 다시 빌드 한다.
+
+
+
+성공적으로 완료된 화면을 볼 수 있다.
+
+
+
+Docker Hub에서 정상적으로 생성된 이미지를 확인 할수 있다.
+
+
+
+* Docker build에 에러가 발생하는 경우가 있는데 Jenkins plugins가 정상적을
+ 설치가 안되어 있을수 있다.
+ docker 검색하여 제대로 설치가 되어 있는지 확인 하고 없으면 재 설치 한다.
+
+
+
+### Docker pull 및 실행 테스트
+
+
+
+터미널로 VM 서버에 접속하여 생성된 도커이미지를 다운로드(pull)하고 실행 (run)
+
+```bash
+docker pull shclub/edu1
+```
+
+
+
+```bash
+docker run -p 40003:8080 shclub/edu1
+```
+Python Flask 가 정상적으로 로드가 된걸 확인 할 수 있다.
+
+
+
+브라우저에서 localhost:40003 호출하여 Hello World 확인
+
+
+
+
+
+- 과제 : github webhook를 통한 빌드 자동화 ( git push 하면 자동 빌드 )
+
+
+
+### Jenkinsfile 설명
+
+
+
+Jenkins 화일에서 github와 docker credential 은 Jenkins 설정에서 Credential을 생성한
+id를 입력하면 된다.
+
+반드시 본인이 만든 값으로 Jenkins파일의 수정해야 함.
+
+
+
+Jenkins에 설정된 credential
+
+
+
+
+Jenkins Stage View 를 통해 Step별 진행 사항을 볼수 있다.
+
+
+
+
+
+
+### Jenkins 환경변수
+
+
+
+env 환경변수는 다음과 같은 형식 env.VARNAME으로 참조될 수 있다. 대표적인 env의 property는 아래와 같다.
+
+
+
+
+
+currentBuild 환경변수는 현재 빌드되고 있는 정보를 담고있다. 보통 readonly 옵션인데 일부 writable한 옵션이 존재한다. 대표적인 currentBuild의 property는 아래와 같다.
+
+
+
+
+환경 변수 사용 예제.
+
+```bash
+pipeline {
+ agent any
+ stages {
+ stage('Example') {
+ steps {
+ echo "Running ${env.BUILD_ID} on ${env.JENKINS_URL}"
+ }
+ }
+ }
+}
+```
+
+
+
+
+
+### Tag를 사용한 Jenkins 빌드
+
+
+
+Tag를 사용한 빌드는 운영(Release)를 위해 주로 사용하며 Tagging하는 순간의 snapshot 이다.
+
+Git Parameter에서 Tag를 선택하고의 default는 RB.0.1 을 임으로 설정한다.
+
+
+
+
+
+GitHub 로 이동 한후 Repository를 선택 한 후 code Tab 으로 들어간다.
+Tags 아이콘을 클릭한다.
+
+
+
+Tags를 선택하고 Create new release 버튼을 클릭한다.
+
+
+
+Choose a tag를 클릭하면 tag이름을 입력하는 text 박스가 나온다.
+
+
+
+생성하고 싶은 Tag 이름을 입력한다.
+jenkins pipeline에서 RB.0.1을 기본값으로 설정을 해서 같은 이름으로 입력한다.
+입력창 아래 생성된 + Create new tag : RB.0.1 클릭
+
+
+
+Title 값을 입력 ( 원하는 이름 아무거나 입력 ) 하고 Publish release 버튼을 클릭한다
+
+
+
+
+
+Jenkins 대쉬보드에서 Build with Parameters 선택하면 Tag이름이 RB.0.1로 기본값이 설정된다.
+
+Tag를 선택하고 Build 버튼을 클릭하면 해당 Tag의 소스로 빌드가 된다.
+
+
+
+
\ No newline at end of file
diff --git a/chapter2.md b/chapter2.md
new file mode 100644
index 0000000..7afd0c8
--- /dev/null
+++ b/chapter2.md
@@ -0,0 +1,534 @@
+
+# Chapter 2
+
+Jenkins CI 구성 요소인 Git , Docker에 대해서 자세한 설명과 함께 Docker Compose 에 대해서도 실습을 한다.
+
+
+
+1. GitHub Action와 Workflow 실습하기
+
+2. Docker compose 설치 및 WordPress, SQL 구성 예제 실습
+
+
+
+## Github action 과 workflow 사용하여 도커 이미지 생성 ( Goodbye Jenkins )
+
+
+
+### GitHub Package
+
+
+
+github 에서도 Packages 라는 이름으로 도커 레지스트리를 기능을 지원한다.
+현재 private은 500 메가 까지는 제공을 하고 있다.
+
+docker 이미지 이름은 앞에 ghcr.io가 붙는다.
+github의 본인 계정으로 이동하면 packages tab을 볼수 있다.
+
+
+
+
+
+### GitHub Docker 이미지 빌드 후 github package 에 push
+
+
+
+우리가 그동안 Jenkins 를 통하여 Docker Build 를 수행 했지만 이제는
+Github의 Action , workflow 기능을 이용하여 빌드를 수행해본다.
+
+
+
+https://github.com/shclub/edu7 를 본인의 github에 fork 한다.
+
+Actions tab 을 클릭한다.
+
+
+
+템플릿 목록이 나오고 먼저 Github package 에 push 하기 위해서 Publish Docker Container Template을 선택한 후 configure 를 클릭한다.
+- IOS 나 Android의 경우는 search 메뉴에서 검색한다.
+
+
+
+docker-publish.yml 화일이 아래 처럼 생기고 schedule 부분의 2개 라인만
+주석 처리하고 Start commit을 클릭한다.
+
+
+
+./github/workflows 폴더가 생성이 되고 docker-publish.yml 화일이 생성 된것을 확인 할수 있다.
+
+
+
+다시 Actions Tab을 클릭한다.
+Docker 라는 workflow 가 생성이되고 오른편에 pipeline 이 실행 되고 있는것을 확인할 수있다.
+
+
+
+정상적으로 수행이되면 파란색으로 아이콘이 변경이되고 에러가 발생하면 붉은색으로 나온다.
+
+
+
+해당 파이프라인 클릭 ( create Docker-publish.yml ) 하면 빌드 화면으로 넘어가고 Build를 클릭하면 오른편에 파이프라인 세부 로그를 볼 수있다.
+
+
+
+생성된 빌드 이미지를 보기 위해서는 본인 계정을 클릭한다.
+
+
+
+repository에서도 오른편에서 package 를 통해 확인 할 수도 있다.
+
+
+
+Packages를 클릭하면 신규로 생성된 도커 이미지를 확인 할 수 있다.
+
+
+
+해당 도커 이미지를 클릭하면 docker pull을 위한 도커 이미지를 명령어를 확인 할 수 있고 오른편에는
+package를 설정할수 있다.
+
+기본 설정은 public 이다.
+
+
+
+생성된 도커를 터미널 창에서 아래와 같이 실행한다.
+기본 tag는 master 이다.
+
+
+```bash
+root@jakelee:~# docker run ghcr.io/shclub/edu7:master
+ * Serving Flask app 'app' (lazy loading)
+ * Environment: production
+ WARNING: This is a development server. Do not use it in a production deployment.
+ Use a production WSGI server instead.
+ * Debug mode: off
+ * Running on all addresses (0.0.0.0)
+ WARNING: This is a development server. Do not use it in a production deployment.
+ * Running on http://127.0.0.1:5000
+ * Running on http://172.17.0.2:5000 (Press CTRL+C to quit)
+ ```
+
+
+
+
+### GitHub Docker 이미지 빌드 후 docker hub 에 push
+
+
+
+GitHub package 가 아닌 Docker hub 에 push 해본다.
+
+Actions Tab으로 이동하여 New workflow를 클릭한다.
+
+
+
+아래의 내용을 복사한다.
+
+```bash
+name: Publish Docker image
+
+on:
+# release:
+# types: [published]
+ push:
+ branches: [ master ]
+ # Publish semver tags as releases.
+# tags: [ 'v*.*.*' ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ push_to_registry:
+ name: Push Docker image to Docker Hub
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out the repo
+ uses: actions/checkout@v3
+
+ - name: Log in to Docker Hub
+ uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
+ with:
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_PASSWORD }}
+
+ - name: Extract metadata (tags, labels) for Docker
+ id: meta
+ uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
+ with:
+ images: shclub/edu7
+
+ - name: Build and push Docker image
+ uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
+ with:
+ context: .
+ push: true
+ tags: shclub/edu7
+ #${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+```
+
+아래와 같이 생성이 되면 화일명을 docker-hub-publish.yml로 변경을 하고 image 이름을 원하는 이름으로 변경한다.
+본인 docker hub id 를 사용한다.
+
+```bash
+#before
+ - name: Extract metadata (tags, labels) for Docker
+ id: meta
+ uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
+ with:
+ images: shclub/edu7
+
+ - name: Build and push Docker image
+ uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
+ with:
+ context: .
+ push: true
+ tags: shclub/edu7
+ labels: ${{ steps.meta.outputs.labels }}
+#after
+ - name: Extract metadata (tags, labels) for Docker
+ id: meta
+ uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
+ with:
+ images: <본인 도커 계정>/edu7 <-- 수정
+
+ - name: Build and push Docker image
+ uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
+ with:
+ context: .
+ push: true
+ tags: <본인 도커 계정>/edu7 <-- 수정
+ labels: ${{ steps.meta.outputs.labels }}
+```
+
+
+
+start commit 버튼을 클릭하면 화일이 신규로 생긴것을 확인할 수가 있고 빌드가 수행이 된다.
+
+
+
+Actions Tab 으로 이동하면 Publish Docker image 가 생성이 되고 빌드 파이프 라인이 성공 1개 에러 1개가 발생 한 것을 확인 할 수 있다.
+
+
+
+에러를 클릭하면 세부 파이프라인 창으로 이동을 하고 오른편 화면에 에러가 난 곳을 확장 하여 에러메시지를
+확인한다.
+
+에러 메시지는 Github Repository (edu7)에 도커 허브 credential을 만들지 않아서 발생한 에러이다.
+
+
+
+도커 허브 Credential을 만들기 위해서 setting -> secret 으로 이동하여 Action을 클릭한다.
+
+
+
+New Repository Secret를 클릭한다.
+
+
+
+docker-hub-publish.yml 에 생성한 것처럼 설정을 한다.
+
+```bash
+with:
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_PASSWORD }}
+```
+
+아래와 같이 secret을 생성한다.
+
+
+
+2개의 secret이 생성된 것을 확인 할수 있다.
+
+
+
+Actions Tab 으로 이동하여 Publish Docker image 를 선택하고 에러난 화면을 클릭하여 세부 파이프라인 창으로 이동한다.
+
+
+
+오른쪽 상단에 Re-run failed job을 선택한다.
+
+
+
+Re-run jobs를 클릭한다.
+
+
+
+다시 파이프라인을 재실행을 한다.
+
+
+
+성공으로 빌드 된것을 확인 할 수 있다.
+
+
+
+도커 허브로 이동하여 생성된 이미지를 확인한다.
+
+
+
+
+
+
+### 수동으로 Actions workflow 실행
+
+
+
+workflow는 schedule 또는 event trigger를 통해서 동작을 하지만 수동으로 원할때만 빌드 할수 있도록 구성을 할 수 있다.
+
+
+
+docker-hub-publish.yml 화일에서 on 아래에 아래와 같이 추가해 준다.
+기존의 값은 주석 처리한다. ( jobs 아래 내용은 수정하지 않는다 )
+
+```bash
+on:
+ workflow_dispatch:
+ inputs:
+ name:
+ description: "TAG"
+ required: true
+ default: "master"
+# schedule:
+# - cron: '25 2 * * *'
+# push:
+# branches: [ master ]
+# # Publish semver tags as releases.
+# tags: [ 'v*.*.*' ]
+# pull_request:
+# branches: [ master ]
+```
+
+Commit을 하고 Actions Tab으로 이동하면 아래와 같이 Run workflow 버튼이 생성된 것을 확인 할 수 있다.
+
+버튼을 클릭하면 설정한 Input 값이 나오고 Run을 하면 실행이 된다.
+
+
+
+
+## Docker Compose
+
+
+
+### Docker Compose 개요
+
+Docker compose란, 여러 개의 컨테이너로부터 이루어진 서비스를 구축, 실행하는 순서를 자동으로 하여, 관리를 간단히하는 기능이다.
+
+ Docker compose에서는 compose 파일을 준비하여 커맨드를 1회 실행하는 것으로, 그 파일로부터 설정을 읽어들여 모든 컨테이너 서비스를 실행시키는 것이 가능하다.
+
+### Docker Compose를 사용하기까지의 주요한 단계
+
+Docker compose를 사용하기 위해서는, 크게 나눠 아래의 세 가지 순서로 이루어진다.
+
+1) 각각의 컨테이너의 Dockerfile를 작성한다(기존에 있는 이미지를 사용하는 경우는 불필요).
+
+2) docker-compose.yml를 작성하고, 각각 독립된 컨테이너의 실행 정의를 실시한다(경우에 따라는 구축 정의도 포함).
+
+3) "docker-compose up" 커맨드를 실행하여 docker-compose.yml으로 정의한 컨테이너를 개시한다.
+
+ Docker compose는 start, stop, status, 실행 중의 컨테이너의 로그 출력, 약간의 커맨드의 실행이과 같은 기능도 가지고 있다.
+
+
+
+### Docker Compose 설치
+
+본인 VM에 터미널로 접속하여 아래 명령어를 입력한다.
+
+```bash
+apt-get update && apt install docker-compose
+```
+중간에 추가 설치 내용이 나오면 Y를 입력하고 엔터를 친다.
+
+
+
+도커 컴포즈 버전을 확인하고 아래와 같이 나오면 정상적으로 설치가 된 것이다.
+
+```bash
+docker-compose --version
+```
+
+
+### Docker Compose yaml
+
+도커 실행 명령어를 yml 파일로 스크립트 문서화 하여 관리한다.
+
+이번 예제는 워드프레스(WordPress)라는 오픈 소스 블로그 소프트웨어 이고
+워드 프레스를 통해서 PHP기반의 웹페이지를 만들수 있다.
+기본적으로 Mysql DB를 내장하고 있다.
+
+
+터미널로 접속하여 새로운 폴더를 하나 생성하고 생성된 폴더로 이동한다.
+vi 에디터로 docker-compose.yml 를 생성한다.
+
+```bash
+mkdir edu2-1
+cd edu2-1
+vi docker-compose.yml
+```
+
+
+
+아래의 내용을 복사하여 docker-compose.yml에 붙여 넣기한다.
+esc 눌러주고 :wq를 입력하여 저장하고 나온다.
+
+
+```yaml
+version: '3.3'
+
+services:
+ db:
+ image: mysql:5.7
+ volumes:
+ - ./mysql:/var/lib/mysql
+ restart: always
+ environment:
+ MYSQL_ROOT_PASSWORD: wordpress
+ MYSQL_DATABASE: wordpress
+ MYSQL_USER: wordpress
+ MYSQL_PASSWORD: wordpress
+ wordpress:
+ depends_on:
+ - db
+ image: wordpress:latest
+ ports:
+ - "40004:80"
+ restart: always
+ environment:
+ WORDPRESS_DB_HOST: db:3306 // mysql 기본 설정
+ WORDPRESS_DB_USER: wordpress
+ WORDPRESS_DB_PASSWORD: wordpress
+ WORDPRESS_DB_NAME: wordpress
+ volumes:
+ - ./wp:/var/www/html
+```
+
+맥 M1 사용자는 M1용 mysql 도커 이미지가 없어 아래의 샘플로 테스트 한다.
+
+
+```yaml
+version: '3.3'
+services:
+ frontend:
+ image: ghcr.io/shclub/edu12-3:master
+ depends_on:
+ - backend
+ environment:
+ API_URL: http://backend:8080
+ ports:
+ - 3000:80
+ backend:
+ image: ghcr.io/shclub/edu12-4:master
+ container_name: backend
+ environment:
+ SPRING_PROFILES_ACTIVE: dev
+ ports:
+ - 8092:8080
+ db:
+ image: mysql
+ restart: always
+ volumes:
+ - ./mysql/:/var/lib/mysql/
+ ports:
+ - 3306:3306
+ environment:
+ MYSQL_ROOT_PASSWORD: edu1234
+ MYSQL_USER: edu
+ MYSQL_PASSWORD: edu1234
+ MYSQL_DATABASE: edu
+```
+
+
+Docker compose 명령어를 사용하여 컨테이너를 실행한다.
+
+```bash
+docker-compose up -d
+```
+
+- 현재 디렉토리에 있는 docker-compose 파일을 실행하여, docker-compose 파일에 정의된 컨테이너를 실행한다.
+- -d 옵션을 주면 detached(백그라운드)으로 실행한다.
+- –force-recreate 옵션으로 컨테이너를 새로 만들 수 있다.
+- –build 옵션으로 도커 이미지를 다시 빌드한다. (build로 선언했을 때만)
+
+
+
+
+Docker ps를 해보면 2개의 컨테이너가 떠 있는 것을 확인 할 수 있다.
+
+```bash
+docker ps 또는 docker-compose ps
+```
+
+
+
+
+해당 폴더를 보면 .wp 와 .mysql 폴더가 생성 된 것을 확인 할수 있다.
+
+```bash
+ls
+```
+
+
+docker compose 기동시에 volumes 설정이 로컬 폴더와 컨테이너 폴더를 연결 한것 을 볼수 있다.
+
+
+
+브라우저를 통해서 http://localhost:40004로 접속하여 정상적으로 로드 된걸 확인 할 수 있다.
+
+도커 컴포즈 명령어
+- 컨테이너 종료 : docker-compose down
+- 컨테이너 정지 : docker-compose stop
+- 컨테이너 로그 보기 : docker-compose logs
+- 컨테이저 재시작 : docker-compose restart
+- stop으로 정지 된 컨테이너 시작 : docker-compose start
+- 도커 네트웍 지우기 : docker network prune
+
+
+
+다른 docker-compse를 테스트 하기 위해서는 폴더를 새로 만든 후 docker-compose.yaml를 만들고 해당 폴더에서 명령어를 수행한다.
+
+
+
+## 과제
+
+
+
+### 과제 1
+
+
+ docker compose로 구성한 mysql container 접속하여 로그인 한 후 wordpress db에 customer 테이블을 생성해 본다.
+
+
+
+### 과제 2
+
+mysql container 접속하여 로그인 한 후 wordpress db에
+아래 테이블 script를 로컬에 저장된 화일을 사용하여 test 테이블을 생성해 본다.
+
+ https://github.com/shclub/edu1/blob/master/test.sql 화일을 다운 받는다.
+
+
+ - TIP : 화일 이동 방법은 cp 명령어 사용.
+
+ 호스트 -> 컨테이너
+ ```bash
+ docker cp [host 파일경로] [container name]:[container 내부 경로]
+ ```
+ 컨테이너 -> 호스트
+
+ ```bash
+ docker cp [container name]:[container 내부 경로] [host 파일경로]
+ ```
+
+
+
+### 과제 3
+
+금일 실습한 Dockerfile과 docker-compose.yml 화일을 git 명령어를 사용하여 edu2에 push 한다.
+
+
+
+### 과제 4
+
+docker 컨테이너 GUI 관리 툴인 portainer를 설치하고 웹에서 접속하여
+ 모니터링한다.
+ - url 참고 : https://docs.portainer.io/v/ce-2.11/start/install/server/docker/linux
+ - 웹 포트는 40005로 expose 한다 ( https 9443 포트 변경 필요 ).
+ - 웹브라우저 접속은 https://(본인VM Public IP):40005
+ admin 비밀번호 신규로 생성 (8자리 이상) 한다.
\ No newline at end of file
diff --git a/chapter3.md b/chapter3.md
new file mode 100644
index 0000000..a29798b
--- /dev/null
+++ b/chapter3.md
@@ -0,0 +1,657 @@
+# Chapter 3
+
+
+kubernetes 는 FlyingCube 2.0 ( OKD 4.7 ) 로 실습을 진행하다.
+
+1. kubernetes 설명
+
+2. Kubernetes 의 API 서버 보안 ( Service Account )
+
+3. Kubernetes 의 Downward API
+
+참고 : https://subicura.com/k8s/
+
+
+
+
+
+## kubernetes
+
+### 쿠버네티스 시작하기
+
+개발 환경에서 당연하게 사용해왔던 쿠버네티스에 대해 이해하고, 왜 쿠버네티스를 사용하는지 알아보자.
+
+
+
+개요
+- 컨테이너 오케스트레이션의 개념과 사용하는 이유 그리고 특징에 대해 이해한다.
+
+소개
+- 쿠버네티스는 컨테이너 오케스트레이션 툴의 한 종류이며 엄청난 인기로 사실상 표준으로 사용된다.
+- 컨테이너 오케스트레이션은 복잡한 컨테이너 환경을 효과적으로 관리하기 위한 도구이다.
+
+배경
+- 서버의 상태를 편하게 관리하기 위한 노력으로 도커 컨테이너가 등장했다.
+- 그러나 관리하는 서버 컨테이너 수가 점점 증가하며 관리가 힘들다는 문제가 생겼다.
+- 그래서 등장한 것이 컨테이너 오케스트레이션이다.
+
+컨테이너의 특징
+
+- 가상머신과 비교하여 컨테이너 생성이 쉽고 효율적
+- 컨테이너 이미지를 이용한 배포와 롤백이 간단
+- 언어나 프레임워크에 상관없이 애플리케이션을 동일한 방식으로 관리
+- 개발, 테스팅, 운영 환경을 물론 로컬 피시와 클라우드까지 동일한 환경을 구축
+- 특정 클라우드 벤더에 종속적이지 않음
+
+
+
+
+### 컨테이너 오케스트레이션
+
+
+
+컨테이너 오케스트레이션을 사용하지 않는다면
+
+- 배포(Deployment)
+
+
+
+ - 각 서버의 ip를 찾고 각 서버에 ssh로 접속해서 docker 명령어로 컨테이너를 실행 및 종료하는 수고가 든다.
+ - 만약 새로운 컨테이너를 실행하려면 빈 서버(여유있는)에 실행 하는것이 리소스 절약을 위해 좋으나, 이를 확인하기 위한 모니터링 툴이 없으면 빈 서버를 일일히 찾기도 힘들다.
+ - 서버를 배포할 때(version upgrade 또는 rollback) 모든 서버를 한번에 배포하는 방법이 필요하다.
+
+
+
+- 서비스 검색(Service Discovery)
+
+
+
+ - 보통의 구조라면 프록시 서버가 있고 로드밸런서를 통해 서버에 적절히 부하를 분산한다.
+ - 그러나 로드밸런서와 서버의 ip 설정과 같은 부분이 관리자가 직접 관리해야하는 포인트였다.
+ - 마이크로서비스 환경이 유행처럼 등장하며 서버가 점점 많아지고 서버의 ip가 업데이트로 변경되고 하면서 관리자가 이를 모두 관리하는 것은 쉽지 않았다.
+
+
+
+- 서비스 노출(Gateway)
+
+
+
+ - NginX와 같이 외부에 노출된 프록시 서버를 두고, 프록시 서버로 들어오는 host 요청에 따라 내부 컨테이너(서버)에 연결하는데 이 과정에 자동화가 필요했다.
+
+
+
+- 서비스 이상과 부하 모니터링
+
+
+
+ - 갑자기 컨테이너가 죽은 경우에 이전에는 일일히 로그 확인해서 다시 서버를 띄워야 했다.
+ - 같은 서버 컨테이너가 3개 돌다가 하나의 컨테이너가 죽으면 남은 2개의 서버에 부하가 생긴다.
+ - 서버가 죽지는 않았는데 트래픽이 많아지면 부하가 걸려 느려졌다.
+ - 트래픽에 따라 적절하게 서버를 늘려야 함
+ - 위와 같은 상황으로 자동화가 필요했다.
+
+
+
+
+컨테이너 오케스트레이션
+
+- 위와같은 문제로 많은 컨테이너를 효율적으로 관리하기 위한 기술이 컨테이너 오케스트레이션이다.
+- 컨테이너 오케스트레이션 복잡한 컨테이너 환경을 효과적으로 관리하기 위한 도구이다.
+
+
+
+컨테이너 오케스트레이션 특징
+
+- 클러스터(Cluster)
+
+
+
+ - 중앙 제어
+ - 이전에는 서로 다른 노드의 CPU와 RAM 상태를 각각 관리했었다.
+ - 그러나 노드 수가 증가하면 힘들기 때문에, 컨테이너 오케스트레이션에서는 합쳐서 추상화하여 클러스트 단위로 관리한다.
+ - 클러스터 하나하나의 노드에 ssh로 직접 접속하기 힘들기때문에 프록시처럼 앞에 마스터 서버를 두고 마스터서버가 각 노드에 알아서 명령을 보낸다.
+
+ - 네트워킹
+ - 클러스터 내 노드끼리는 서로 통신이 잘되어야 한다.
+ - 노드 스케일
+ - 노드 스케일이 커지더라도 잘 돌아가기 위해서는 정교한 설계가 필요하다.
+
+
+
+- 상태 관리(State)
+
+
+
+ - 트래픽이 증가해 서버를 새로 늘리거나, 하나의 서버에 장애가 발생했을 때 감지하고 자동으로 서버를 늘려준다.
+ - 오토 스케일링
+
+
+
+- 배포 관리(Scheduling)
+
+
+
+ - 자원이 여유가 있는 서버에 알아서 적절하게 띄워준다.
+
+
+
+- 배포 버전관리(Rollout & Rollback)
+
+
+
+ - 전체 컨테이너에 대한 롤아웃과 롤백을 중앙에서 관리한다.
+
+
+
+- 서비스 등록 및 조회(Service Discovery)
+
+
+
+ - 새로 등록된 서비스 ip나 변경된 ip를 자동으로 관리해줘서 관리자가 하나하나 수정할필요가 없다.
+
+
+
+
+- 볼륨 스토리지(Volume)
+
+
+
+ - 각 서버에 필요한 스토리지(AWS EBS, GCE 등)를 설정으로 연결할 수 있다.
+ - AWS에서 EC2에 스토리지 일일히 하나하나 붙이고 그럴 필요가 없다는 말이다.
+
+
+
+왜 쿠버네티스인가
+
+- 컨테이너 관리도구가 많이 생기고 했으나, 쿠버네티스가 표준처럼 등장했다.
+- 오픈소스
+
+ - 컨테이너를 쉽고 빠르게 배포/확장하고 관리를 자동화해주는 오픈소스 플랫폼
+ - 구글에서 만듬 (구글은 1주일에 20억개 컨테이너 생성한다.)
+
+- 엄청난 인기
+ - 점유율이 높고 그렇기에 라이브러리 또는 레퍼런스가 많다.
+
+- 무한한 확장성
+ - 쿠버네티스 위에서 머신러닝, CI/CD, 서비스. 서버리스 등 다양한 서비스가 동작
+ - 쿠버네티스만 설치되어 있으면 거기에 서비스를 올리기 쉬움
+
+- 사실상 표준(de facto)
+ - 많은 오케스트레이션이 있지만 사실상 표준이 되어가고 있음
+ - 도커도 자체 오케스트레이션이 있지만 쿠버네티스의 인기로 인해 어쩔수없이 쿠버네티스 지원
+ - AWS(Elastic Kubernetes Service), Azure(Azure Kubernetes Service), Google(Google Kubernetes Engine)와 같이 대표적인 클라우드 기업들이 쿠버네티스를 매니지드 서비스로 제공하고 있음
+
+
+
+
+### kubernetes API 서버 보안
+
+
+
+쿠버네티스에서 API 서버 보안은 왜 필요할까?
+
+쿠버네티스에서는 이론적으로 파드 외부 또는 내부에서 API 서버로 적절한 요청을 하면 어떤 리소스던 생성, 삭제, 수정, 조회가 가능하다.
+
+그런데 만약 개발자의 코딩 실수로 어떤 파드에서 아무 관련이 없는 다른 파드를 삭제해버릴 수 있다면 큰 문제가 될 것이다.
+
+그렇기 때문에 사용자 또는 파드에게 적절하게 권한을 부여하는 기능은 보안과 안정적인 운영을 위해 필수적이다.
+
+회사에서 여러개의 팀이 하나의 공용 클러스터를 함께 사용하는 멀티테넌트 환경에서는 이러한 권한의 분리가 더욱 중요하다.
+
+쿠버네티스 클러스터는 API 서버가 사용자나 Pod 의 요청을 받을 때 명시적으로 설정된 권한만 허용하고, 그 이외의 모든 권한은 허용하지 않도록 동작한다.
+
+
+
+#### Service Account 란?
+
+
+
+서비스 어카운트(Service Account) 는 Kubernetes 의 파드에서 API 서버에 요청을 보냈을 때 이 "파드"를 식별하기 위한 리소스다. (사용자를 식별하는데 사용되지는 않는다)
+
+파드에서 API 서버에 요청을 보내면 이 파드의 정체가 무엇인지 알아야 어떤 권한을 가지고 있는지도 알 수 있고,
+
+이를 기반으로 파드의 요청이 권한에 맞는지를 확인하여 요청을 처리해줄지 말지를 결정할 것이다.
+
+실제로 권한을 정의하고, 설정하는 부분은 이후에 설명할 Role, ClusterRole, RoleBinding, ClusterRoleBinding 의 역할이다.
+
+
+
+#### ServiceAccount 의 특징
+
+
+
+모든 파드는 무조건 하나의 ServiceAccount 와 매핑이 되어야 실행될 수가 있다.
+
+그런데 ServiceAccount 를 만들지 않고, 파드의 매니페스트에 ServiceAccount 를 명시적으로 적어주지 않아도 파드가 잘 생성이 되고 실행 되는 것을 보고 의아할 수도 있다.
+
+사실 이것은, 쿠버네티스의 ServiceAccount Controller 가 모든 네임스페이스에 default 라는 이름의 서비스어카운트가 있도록 자동 생성해주며,
+
+ServiceAccount `Admission Controller` 가 파드의 매니페스트에 명시적으로 서비스어카운트를 정의하지 않으면 default 서비스어카운트를 매핑해주기 때문에 가능한것이다.
+
+뿐만 아니라, 서비스어카운트는 mountable secrets 에 지정한 시크릿만 파드에 마운트할 수 있도록 강제하는 기능과,
+
+Image pull secrets 기능을 통해 프라이빗 이미지 레지스트리에서 이미지를 가져올 수 있도록 하기 위한 시크릿을 이 서비스어카운트를 사용하는 파드에 자동으로 마운트시켜주는 기능도 가지고 있는데
+
+image pull secrets 기능도 ServiceAccount Admission Controller 가 수행한다.
+
+
+
+#### ServiceAccount 의 동작 방식
+
+
+
+kubectl create sa 명령어로 서비스어카운트를 생성할 수 있다.
+
+
+`Token Controller` 는 서비스어카운트가 생성될 때마다 자동으로 kubernetes.io/service-account-token 타입의 Secret 을 생성하여 매핑시켜준다.
+
+```bash
+root@jakelee:~# kubectl get sa
+NAME SECRETS AGE
+default 1 18d
+root@jakelee:~# kubectl get secret
+NAME TYPE DATA AGE
+default-token-p9fpr kubernetes.io/service-account-token 3 18d
+root@jakelee:~# kubectl describe secret default-token-p9fpr
+Name: default-token-p9fpr
+Namespace: default
+Labels:
+Annotations: kubernetes.io/service-account.name: default
+ kubernetes.io/service-account.uid: ef9e4103-6663-4fcb-a12f-e2626d7e9666
+
+Type: kubernetes.io/service-account-token
+
+Data
+====
+token: eyJhbGciOiJSUzI1NiIsImtpZCI6ImhvbzBnMGhOX1VBMFp6MVN4ejFoX0RoVEtBX25lZUNkX291d3BnLUNWWVEifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tcDlmcHIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImVmOWU0MTAzLTY2NjMtNGZjYi1hMTJmLWUyNjI2ZDdlOTY2NiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.wHfW2CGCePikcfSumoM9GvUIQS_8gI_wqKRnz9XzRFJ7rBl2iZNM7h47rPhHcA-uR61G6C6mzFHzb0zVYfo_PQAuddYF1PB3FZ9DmlylX3J1ne2KBQhh_f88FXmRCQZbiK5DS_GiR1Ys2UWzykLQFXtHDrQ7n_oy3Bq3poi4lDObg_TIH2rSyrhADebDqRXHOUZ5GGNV3Y7qwTvl9hFhbPT6Z8VO0mGgpHvv52XmNzHORUhwZRsMBLlMh19DNVTizvRlhemTZJMM71d8wJ9KG9rFhDwodWzuWgCYNxklWDvhDCsuJSj1QHei-9QSdqy2YTp5Qddcux7nMgyufQZT9Q
+ca.crt: 570 bytes
+namespace: 7 bytes
+```
+
+
+Token Controller 에 의해 생성된 Secret 에는 아래와 같은 3가지 데이터가 base64 로 인코딩되어 들어있다.
+
+- ca.crt - API 서버와 통신 시, SSL 인증을 위한 증명서
+- token - 서비스어카운트 이름, Secret 이름 등의 정보에 서명한 JWT.
+ API 서버에 요청시 Bearer 토큰으로 사용됨
+- namespace - 네임스페이스
+
+파드 내 애플리케이션은 위의 데이터를 사용하여 API 서버와 통신한다.
+
+API 서버는 요청의 Authorization 헤더에 있는 Bearer token 을 디코딩하여 어느 서비스어카운트를 사용해 보낸 요청인지 식별하게 된다.
+
+
+
+#### RBAC 란?
+
+
+
+어떤 사람, 혹은 파드가 API 서버에 요청을 하면 API 서버는 인증(Authentication)과 인가(Authorization)를 수행한다.
+
+인증은 접근 가능 여부를 확인하는 것이고, 인가는 접근 가능한 요청에 대해 요청된 자원에 접근할 수 있는지를 확인하는 것이다.
+
+RBAC(Role-Based Access Control) 는 API 서버가 인가를 수행하는 여러 방법 중 하나다.
+
+쿠버네티스는 다음과 같은 인가 방식을 제공한다.
+
+- Node
+ - Kubelet 에 의한 요청에 대한 인가를 위한 방식
+- ABAC(Attribute-Based Access Control)
+ - 리소스의 속성에 따라 인가를 하는 방식
+- RBAC
+ - Role 을 기반으로 인가를 하는 방식
+- Webhook
+ - 외부 API 를 통해 인가를 하는 방식
+
+이 중에서 RBAC가 표준이며, kubernetes 1.8.0 부터는 대부분의 클러스터에서 기본적으로 사용하는 방식이다.
+
+RBAC 는 `특정 주체(subject) 가 특정 대상(url, resource 타입, 혹은 특정 resource) 에 대해 특정 행위(verb)` 를 할 수 있는지를 지정하는 방식이다.
+
+
+
+#### Role 과 RoleBinding
+
+
+
+
+어떤 사람, 혹은 파드가 API 서버에 요청을 하면 API 서버는 인증
+
+Role 은 대상(resource 등) 과 행위(verb) 를 지정하며, RoleBinding 은 주체(subject) 를 지정한다.
+
+여기서 주체는 3가지(User, Group, ServiceAccount) 중 한가지가 된다.
+
+대상은 보통 resource 의 타입을 정하는데, resourceName 으로 특정 리소스를 지정할 수도 있다.
+
+이후에 설명할 ClusterRole 에서는 리소스가 아닌 URL 을 지정할 수도 있다. /healthz 와 같이 특정 리소스에 대한 요청이 아닌 경우도 있기 때문이다.
+
+행위는 아래 표와같이 API 요청에 사용된 HTTP 메서드에 따라 특정 행위에 매핑되는데,
+
+
+
+
+행위에 대한 대상이 리소스라면 행위를 verb 로 적어주고, URL 이라면 HTTP 메소드로 적어준다.
+
+이름 그대로 Role 은 역할이고, RoleBinding 은 이러한 역할을, 역할을 수행하는 주체에 연결시켜 주는 것이다.
+
+
+
+#### Role 과 RoleBinding 의 특징
+
+
+
+Role 과 RoleBinding 은 특정 네임스페이스에 종속된 개념이다. 그렇기 때문에 RoleBinding 은 다른 네임스페이스의 Role 을 바인딩해줄 수는 없다.
+
+하지만, RoleBinding 이 같은 네임스페이스의 Role 을 다른 네임스페이스의 subject 에게 바인딩해 줄 수는 있다.
+
+그래서 RoleBinding 에 subject 들을 명시할 때는 name 과 namespace, 그리고 kind(user/group/serviceaccount) 를 함께 명시한다.
+
+하나의 Role 은 여러개의 RoleBinding 에 의해 바인드될 수 있고, 하나의 RoleBinding 은 하나의 Role 만 참조할 수 있다.
+
+즉, `Role 과 RoleBinding 은 일대다(one-to-many)` 관계다.
+
+반면 하나의 RoleBinding 은 하나의 Role 을 여러 주체에 연결시켜 줄 수 있고, 하나의 주체는 여러개의 RoleBinding 에 의해 권한이 부여될 수 있다.
+
+즉, `RoleBinding 과 Subject(ServiceAccount 등) 는 다대다(many-to-many)` 관계다.
+
+
+
+#### Role 과 RoleBinding 만들기
+
+
+
+Role 과 RoleBinidng 을 만드는 방법은 크게 두가지가 있다.
+
+하나는 매니페스트를 통해 만드는 방법이고, 나머지 하나는 kubectl create 명령어를 통해 만드는 방법이다.
+
+다음과 같이 YAML 파일을 작성하지 않고도 직접 verb, resource, role, serviceaccount 등을 인자로 주어 Role 과 RoleBinding 을 생성할 수 있다.
+
+먼저 아래 소스를 사용하여 role을 생성합니다.
+
+```bash
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ namespace: default
+ name: service-reader
+rules:
+ - apiGroups: [""]
+ verbs: ["get", "list"]
+ resources: ["services"]
+```
+
+### kuKubernetes 의 Downward API
+
+
+
+#### Downward API 란?
+
+
+
+애플리케이션이 실행되기 전에 이미 알고있는 속성이나 설정 값들은 ConfigMap 이나 Secret 으로 파드에 전달할 수 있지만,
+
+파드의 이름, 파드의 IP, 파드가 실행되는 노드의 이름 등 실제로 파드가 생성 및 실행이 되기전에는 알 수 없는 속성들도 존재한다.
+
+물론 파드의 레이블이나 어노테이션과 같은 일부 속성들은 파드 생성 이전에도 알 수 있지만,
+
+파드 내에서 정보를 사용하고 싶다는 이유로 이미 설정되어 있는 속성을 ConfigMap 등을 통해 중복하여 정의하고 싶지는 않을 것이다.
+
+이런 속성들을 컨테이너에서 실행 중인 애플리케이션에서 알아내려면 어떻게 해야할까?
+
+이 때 사용되는 것이 Downward API 이다.
+
+Downward API 는 단순히 환경변수, 또는 파일(downwardAPI 볼륨을 통해) 로 위와 같은 속성들을 컨테이너에서 손쉽게 사용할 수 있도록 하는 기능일 뿐이다.
+
+Downward API 를 통해 전달할 수 있는 정보는 다음과 같다.
+
+- 파드의 이름
+- 파드의 IP 주소
+- 파드가 속한 네임스페이스
+- 파드가 실행중인 노드의 이름
+- 파드가 실행 중인 서비스 어카운트 이름
+- 각 컨테이너의 CPU와 메모리 request
+- 각 컨테이너의 CPU와 메모리 limit
+- 파드의 label
+- 파드의 annotation
+
+k8s 에서는 파드가 API server 와 통신할 수 있도록 하기 위해
+각 파드마다 기본적으로 Default token 시크릿 볼륨을 만들어
+
+파드 내 컨테이너의 /var/run/secrets/kubernetes.io/serviceaccount/에 마운트해 주는데,
+
+이 곳에 namespace 라는 파일에 네임스페이스가 적혀있기 때문이다.
+
+```bash
+root@jakelee:~# kubectl get po
+NAME READY STATUS RESTARTS AGE
+flask-edu4-app-74788b6479-nmcvv 1/1 Running 0 16d
+flask-edu4-app-74788b6479-rlght 1/1 Running 0 16d
+hpa-example-deploy-59bf97fcc6-6nxjs 1/1 Running 0 16d
+inspekt-deployment-c8d9f5dcf-2slpx 1/1 Running 0 11d
+mynginx-69d586ff67-bmh6m 1/1 Running 0 10d
+web2-5d47994f45-dmrwr 1/1 Running 0 9d
+pod-test-app 1/1 Running 0 6d20h
+root@jakelee:~# kubectl exec -it pod-test-app sh
+kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
+# ls /var/run/secrets/kubernetes.io/serviceaccount/
+ca.crt namespace token
+# cat /var/run/secrets/kubernetes.io/serviceaccount/namespace
+default
+```
+
+
+
+#### 환경 변수로 전달하기 vs 볼륨으로 전달하기
+
+
+
+Downward API 를 통해 데이터를 전달하기 위한 방법으로는
+
+환경 변수를 통한 방법과 볼륨을 통한 방법, 이렇게 크게 두가지가 있다.
+
+대부분의 경우 환경변수를 통한 방법과 볼륨을 통한 방법 중 어떤 방법을 사용해도 크게 문제가 없지만
+
+약간의 차이점이 있다. 우선 일부 정보들은 둘 중 한가지 방법으로만 얻을 수 있다.
+
+예를 들어 Pod 의 label 과 annotation 은 downwardAPI 볼륨을 통해서만 전달할 수 있다.
+
+그 이유는, Pod 의 label 과 annotation 은 Pod 가 실행되는 동안 수정될 수가 있는데,
+
+이 때 Pod 가 변경된 데이터를 볼 수 있도록 해야 한다.
+
+파드가 실행중인 노드의 이름과 IP 는 환경 변수를 통한 방법으로만 얻을 수 있다.
+
+
+
+#### 환경변수로 전달하기
+
+
+
+
+vi에디터로 downward-env.yaml 화일을 만들어 보자.
+
+```bash
+root@jakelee:~# vi downward-env.yaml
+```
+
+아래 소스를 사용한다.
+
+
+```bash
+apiVersion: v1
+kind: Pod
+metadata:
+ name: downward-env
+spec:
+ containers:
+ - name: main
+ image: busybox
+ command: ["sleep", "99999"]
+ resources:
+ requests:
+ cpu: 15m
+ memory: 100Ki
+ limits:
+ cpu: 100m
+ memory: 20Mi
+ env:
+ - name: POD_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
+ - name: POD_NAMESPACE
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ - name: POD_IP
+ valueFrom:
+ fieldRef:
+ fieldPath: status.podIP
+ - name: NODE_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: spec.nodeName
+ - name: SERVICE_ACCOUNT
+ valueFrom:
+ fieldRef:
+ fieldPath: spec.serviceAccountName
+ - name: CONTAINER_CPU_REQUEST_MILLICORES
+ valueFrom:
+ resourceFieldRef:
+ resource: requests.cpu
+ divisor: 1m
+ - name: CONTAINER_MEMORY_LIMIT_KIBIBYTES
+ valueFrom:
+ resourceFieldRef:
+ resource: limits.memory
+ divisor: 1Ki
+```
+
+pod를 생성한다.
+
+```bash
+root@jakelee:~# kubectl apply -f downward-env.yaml
+pod/downward-env created
+```
+
+```bash
+root@jakelee:~# kubectl get po
+NAME READY STATUS RESTARTS AGE
+pod-test-app 1/1 Running 0 6d20h
+downward-env 1/1 Running 0 12s
+```
+
+pod가 생성이 되면 환경 변수를 조회해 본다.
+
+기본 환경 변수에 POD_NAME , POD_NAMESPACE , POD_IP , POD_NAME이 추가 된 것을 확인 할 수 있다.
+
+```bash
+root@jakelee:~# kubectl exec downward-env env
+kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+HOSTNAME=downward-env
+SERVICE_ACCOUNT=default
+CONTAINER_CPU_REQUEST_MILLICORES=15
+CONTAINER_MEMORY_LIMIT_KIBIBYTES=20480
+POD_NAME=downward-env
+POD_NAMESPACE=default
+POD_IP=10.42.0.130
+NODE_NAME=jakelee
+```
+
+
+
+
+
+#### 볼륨으로 전달하기
+
+
+
+vi에디터로 downward-volume.yaml 화일을 만들어 보자.
+
+```bash
+root@jakelee:~# vi downward-volume.yaml
+```
+
+아래 소스를 사용한다.
+
+
+```bash
+apiVersion: v1
+kind: Pod
+metadata:
+ name: downward-volume
+ labels:
+ foo: bar
+ annotations:
+ key1: value1
+ key2: |
+ multi
+ line
+ value
+spec:
+ containers:
+ - name: main
+ image: busybox
+ command: ["sleep", "9999999"]
+ resources:
+ requests:
+ cpu: 15m
+ memory: 100Ki
+ limits:
+ cpu: 100m
+ memory: 40Mi
+ volumeMounts:
+ - name: downward
+ mountPath: /etc/downward
+ volumes:
+ - name: downward
+ downwardAPI:
+ items:
+ - path: "podName"
+ fieldRef:
+ fieldPath: metadata.name
+ - path: "podNamespace"
+ fieldRef:
+ fieldPath: metadata.namespace
+ - path: "labels"
+ fieldRef:
+ fieldPath: metadata.labels
+ - path: "annotations"
+ fieldRef:
+ fieldPath: metadata.annotations
+ - path: "containerCpuRequestMilliCores"
+ resourceFieldRef:
+ containerName: main
+ resource: requests.cpu
+ divisor: 1m
+ - path: "containerMemoryLimitBytes"
+ resourceFieldRef:
+ containerName: main
+ resource: limits.memory
+ divisor: 1
+```
+
+pod가 생성이 되면 volume을 조회해 본다.
+
+
+```bash
+root@jakelee:~# kubectl get po
+NAME READY STATUS RESTARTS AGE
+pod-test-app 1/1 Running 0 6d20h
+downward-env 1/1 Running 0 14m
+downward-volume 1/1 Running 0 68s
+root@jakelee:~# kubectl exec downward-volume ls /etc/downward
+kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
+annotations
+containerCpuRequestMilliCores
+containerMemoryLimitBytes
+labels
+podName
+podNamespace
+root@jakelee:~# kubectl exec downward-volume cat /etc/downward/podName
+kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
+downward-volume
+```
+
+
+
diff --git a/chapter4.md b/chapter4.md
new file mode 100644
index 0000000..2c3fe96
--- /dev/null
+++ b/chapter4.md
@@ -0,0 +1,285 @@
+
+# Chapter 4
+
+
+ArgoCD는 GitOps를 구현하기 위한 컨테이너에 최적화된 CD 툴
+
+1. GitOps 란 : https://coffeewhale.com/kubernetes/gitops/argocd/2020/02/10/gitops-argocd/
+
+2. ArgoCD 설치 및 기능 설명
+
+3. Github에 배포 설정
+
+4. 배포 실습 ( Blue/Green , Canary , Rollback )
+
+
+
+
+## GitOps
+
+### GitOps 개요
+
+개발자와 운영자의 소통, 협업, 통합을 강조하는 DevOps는 많이들 들어보셨을 겁니다.
+
+GitOps는 DevOps의 실천 방법 중 하나로 애플리케이션의 배포와 운영에 관련된 모든 요소들을 Git에서 관리(Operation) 한다는 뜻입니다.
+
+
+
+아주 간단하게 말해서 GitOps는 Kubernetes Manifest파일을 Git에서 관리하고, 배포할때에도 Git에 저장된 Manifest로 클러스터에 배포하는 일련의 과정들을 의미합니다.
+
+
+
+GitOps 원칙
+
+- 모든 시스템은 선언적으로 선언되어야 함
+“선언적(declarative)”이라 함은 명령들의 집합이 아니라 사실(fact)들의 집합으로 구성이 되었음을 보장한다는 의미입니다.
+쿠버네티스의 manifest들은 모두 선언적으로 작성되었고 이를 Git으로 관리한다면 versioning과 같은 Git의 장점과 더불어, SSOT(single source of truth)를 소유하게 됩니다.
+- 시스템의 상태는 Git의 버전을 따라감
+Git에 저장된 쿠버네티스 manifest를 기준으로 시스템에 배포되기 때문에 이전 버전의 시스템을 배포하고싶으면 git revert와 같은 명령어를 사용하면 됩니다.
+- 승인된 변화는 자동으로 시스템에 적용됨
+한 번 선언된 manifest가 Git에 등록되고 관리되기 시작하면 변화(코드수정 등)가 발생할때마다 자동으로 시스템에 적용되어야 하며, 클러스터에 배포할때마다 자격증명은 필요하지 않아야 합니다.
+- 배포에 실패하면 이를 사용자에게 경고해야 함
+시스템 상태가 선언되고 버전 제어 하에 유지되었을 때 배포가 실패하게되면 사용자에게 경고할 수 있는 시스템을 마련해야합니다.
+
+
+
+GitOps Repository
+
+GitOps Pipeline을 설계할때에는 Git Repository를 최소 두개이상 사용하는 것을 권장합니다.
+
+
+
+
+- App Repo : App 소스코드와, 배포를 위한 Manifest 파일
+- 배포 환경 구성용 Repo : 배포 환경에 대한 모든 Manifest (모니터링, 서비스, MQ 등)들이 어떤 버전으로 어떻게 구성되어있는지 포함
+
+
+
+
+GitOps 배포 전략
+
+두가지 방법이 있습니다.
+
+- Push Type
+Git Repo가 변경되었을 때 파이프라인을 실행시키는 구조입니다.
+
+
+
+ 배포 환경의 개수에 영향을 받지 않으며 접속 정보를 추가하거나 수정하는 것만으로도 간단하게 배포 환경을 추가하거나 변경할 수 있습니다.
+ 아키텍처가 쉬워 많은 곳에서 사용하고 있으나, 보안정보가 외부로 노출될 수 있다는 단점이 있습니다.
+
+- Pull Type
+배포하려는 클러스터에 위치한 별도의 오퍼레이터가 배포역할을 대신합니다.
+
+
+
+ 해당 오퍼레이터는 Git Repo의 Manifest와 배포환경을 지속적으로 비교하다가 차이가 발생할 경우, Git Repo의 Manifest를 기준으로 클러스터를 유지시켜 줍니다.
+
+ 또한 Push Type과 달리 오퍼레이터가 App과 동일한 환경에서 동작중이므로 보안 정보가 외부에 노출되지 않고 실행할 수 있습니다.
+
+
+
+
+### ArgoCD 둘러보기
+
+
+
+ArgoCD를 설치하여 로그인하면 가장 먼저 볼 수 있는 화면은 아래와 같습니다.
+
+지금까지 생성한 배포 App의 리스트를 보여주는 화면입니다. 새로운 배포를 관장하는 App을 생성해 보기 위해 New App 버튼을 눌러보겠습니다.
+
+
+
+새로운 배포을 책임지는 App을 생성하는 화면입니다.
+
+
+
+- Application Name: App의 이름을 적습니다.
+- Project: 프로젝트를 선택하는 필드입니다. 쿠버네티스의 namespace와 비슷한 개념으로 여러 App을 논리적인 project로 구분하여 관리할 수 있습니다.
+- Sync Policy: Git 저장소의 변경 사항을 어떻게 sync할지 결정합니다. Auto는 자동으로 Git 저장소의 변경사항을 운영에 반영하고 Manual은 사용자가 버튼 클릭을 통해 직접 운영 반영을 해줘야 합니다.
+- Repository URL: ArgoCD가 바라볼 Git 저장소를 의미합니다.
+- Revision: Git의 어떤 revision (HEAD, master branch 등)을 바라 볼지 결정합니다.
+- Path: Git 저장소에서 어떤 디렉토리를 바라 볼지 결정합니다. (dot(.)인 경우 root path를, 디렉토리 이름을 적으면 해당 디렉토리의 배포 정의서만 tracking 합니다.)
+- Cluster: 쿠버네티스의 어느 클러스터에 배포할지를 결정합니다.
+- Namespace: 쿠버네티스 클러스터의 어느 네임스페이스에 배포할지를 결정합니다.
+- Directory Recurse: path아래의 디렉토리를 재귀적으로 모니터링하여 변경 사항을 반영합니다.
+
+
+
+아래의 깃헙 레포지토리를 예시로 배포해 보겠습니다. 간단하게 nginx 컨테이너를 생성하고 서비스를 붙여주는 앱입니다.
+
+GitOps repository 예시: https://github.com/shclub/edu5.git
+
+본인의 github에서 위 repository를 fork 합니다.
+
+아래 처럼 deployment.yaml , service.yaml , ingress.yaml이 생성된걸 확인 할 수 있습니다.
+
+ingress.yaml 화일의 host 정보는 본인 VM Public IP로 변경 합니다.
+
+```bash
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: mynginx
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ run: mynginx
+ template:
+ metadata:
+ labels:
+ run: mynginx
+ spec:
+ containers:
+ - image: nginx
+ name: mynginx
+ ports:
+ - containerPort: 80
+```
+
+
+```bash
+apiVersion: v1
+kind: Service
+metadata:
+ name: mynginx
+spec:
+ ports:
+ - port: 80
+ protocol: TCP
+ targetPort: 80
+ selector:
+ run: mynginx
+```
+
+```bash
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: mynginx-traefik-ingress
+ annotations:
+ ingress.kubernetes.io/rewrite-target: "/"
+ kubernetes.io/ingress.class: traefik
+spec:
+ rules:
+ - host: mynginx.210.106.105.165.sslip.io
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: mynginx
+ port:
+ number: 80
+```
+
+url은 본인의 url로 설정한다.
+
+- Application Name: caravan
+- Project: default
+- Sync Policy: manual
+- Repository URL: https://github.com/shclub/edu5.git
+- Revision: HEAD
+- Path: .
+- Cluster: in-cluster
+- Namespace: default
+- Directory Recurse: Unchecked
+
+
+
+우리는 아래와 같이 설정을 한다.
+
+
+
+
+
+
+위와 같이 값을 설정해주고 `Create` 버튼을 클릭합니다.
+
+`SYNC` 버튼을 눌러 ArgoCD가 변경 사항을 확인하여 단일원천의 진실에 따라 운영 환경을 그에 맞게 변경하도록 하겠습니다.
+
+
+
+App을 클릭하면 아래와 같이 Ingress, Service 리소스와 nginx pod가 생성된 것을 UI로 확인하실 수 있습니다.
+
+
+
+파란색 아이콘이 보이면 정상적으로 수행이 된것 있고 mynginx pod 가 정상적으로 생성 되었는지 확인합니다.
+
+```bash
+root@jakelee:~# kubectl get po
+NAME READY STATUS RESTARTS AGE
+flask-edu4-app-74788b6479-qgs2j 1/1 Running 0 6d21h
+flask-edu4-app-74788b6479-l7gkx 1/1 Running 0 6d21h
+flask-edu4-app-74788b6479-nmcvv 1/1 Running 0 6d21h
+flask-edu4-app-74788b6479-f2kcp 1/1 Running 0 6d21h
+flask-edu4-app-74788b6479-rlght 1/1 Running 0 6d21h
+hpa-example-deploy-59bf97fcc6-6nxjs 1/1 Running 0 6d1h
+inspekt-deployment-c8d9f5dcf-2slpx 1/1 Running 0 23h
+mynginx-69d586ff67-bmh6m 1/1 Running 0 6m23s
+```
+
+Ingress를 설정 했기 때문에 web browser 에서 도메인으로 접속해봅니다.
+정상이면 아래와 같이 nginx welcome 화면이 보입니다.
+
+
+
+
+
+앞에 App을 설정할때 sync-policy를 manual 설정하였습니다.
+
+아래에 Auto-Sync 버튼을 활성화하게 되면 Automatic이 되어 매번 사람이 직접 변경사항을 ArgoCD에게 알릴 필요 없이 ArgoCD가 주기적으로 Git 레포지터리의 변경사항을 확인하여 변경된 부분을 적용하게 됩니다.
+
+이때 두가지 옵션을 추가적으로 줄 수 있습니다.
+
+- Prune Resources: 변경 사항에 따라 리소스를 업데이터할 때, 기존의 리소스를 삭제하고 새로운 리소스를 생성합니다. Job 리소스처럼 매번 새로운 작업을 실행해야 하는 경우 이 옵션을 사용합니다.
+
+- Self Heal: 해당 옵션을 활성화 시키면 ArgoCD가 지속적으로 git repository의 설정값과 운영 환경의 값의 싱크를 맞출려고 합니다. 기본적으로 5초마다 계속해서 sync를 시도하게 됩니다. (default timeout)
+
+해당 예시에서는 Auto-sync만 활성화 시켜보겠습니다. 그런 다음, 이제 git repository의 deployment replica 값을 2로 고쳐서 ArgoCD가 자동으로 변경한 값을 운영 환경에 반영하는지 확인해 보겠습니다.
+
+
+
+AppDetail을 클릭하면 하단에 enable auto sync 버튼을 클릭합니다.
+
+
+
+신규로 pod가 하나 생성되어 2개의 pod가 존재하는 것을 확인 할수 있습니다.
+
+
+
+traffic 흐름을 볼수 도 있고
+
+
+
+각 node의 resource 들을 볼 수 있고 pod의 리소스도 확인 가능 하다.
+
+
+
+
+기본적으로 swagger를 제공하고 있어 ArgoCD 의 API를 사용 할 수 있다.
+- 사용법 : http:// < 본인의 argocd URL > / swagger-ui
+
+
+
+
+
+
+기본 사용법
+ - https://argo-cd.readthedocs.io/en/stable/getting_started/#creating-apps-via-ui
+
+
+
+추가 배포 관련 Hands-on은 아래 문서를 참고 한다.
+
+- 문서를 참고한다. [ArgoCD Hands-On ](./argocd_hands_on.md)
+
+
+
+
+
+
+
+
diff --git a/k8s_basic_hands_on.md b/k8s_basic_hands_on.md
new file mode 100644
index 0000000..1e4b674
--- /dev/null
+++ b/k8s_basic_hands_on.md
@@ -0,0 +1,1667 @@
+# k8s Basic Hands-on
+
+kubernetes에서 kubectl를 사용하여 cli 실습을 한다.
+
+1. 실습 전체 개요
+
+2. Kubeconfig 설정 : kubectl 설치 ( 윈도우 / Mac )
+
+3. Kubectl 활용
+
+4. kubernetes 리소스 ( Pod , Service , Deployment 생성 및 삭제)
+
+5. 배포 ( Rolling Update / Rollback )
+
+6. Serivce Expose ( Ingress )
+
+7. 참고 사이트
+ - podman 사용 : https://github.com/chhanz/kubernetes-hands-on-lab
+ - https://github.com/subicura/workshop-k8s-basic/tree/master/guide
+
+
+
+
+## 실습 전체 개요
+
+
+### Deployment, Service, Ingress/Route 흐름 설명
+
+
+
+
+
+
+Ingress/Route : HTTP 나 HTTPS 통신을 클러스터 내부의 서비스에 연결해 주는 도구.
+쉽게 말해 가정집의 공유기와 비슷한 역할을 한다.
+Deployment(pod) 는 Service 와 연결된다고 선술한 바 있다.
+
+Ingress 는 아래 이미지와 같이 Service 와 연결해주면 된다.
+
+
+- Deployment 정의 : Deployment는 메타데이터 및 pod 리플리카 개수, 컨테이너 이미지, 이미지 포트 등을 정의한다.
+
+
+- Service 정의 : Service는 아래와 같이 어노테이션 및 서비스 포트 및 타깃 포트 등을 정의한다.
+
+
+- ingress/Route 정의 : 어노테이션 및 ingress의 서비스 name과 port를 정의한다.
+
+
+
+Deployment 와 Service 그리고 Ingress의 관계가 간단하게 정리된 그림
+
+
+
+
+
+위의 예시를 토대로 Deployment와 Service 그리고 Ingress의 관계를 Flow로 표현한 그림
+
+
+
+
+
+## Kubeconfig 설정 : kubectl 설치 ( 윈도우 / Mac )
+
+### kubectl 설치 ( https://kubernetes.io/ko/docs/tasks/tools/ )
+
+
+
+GUI IDE인 lens 대신 kubectl cli를 통해 실습을 하기 위해 로컬 PC에
+kubectl 을 설치 한다.
+
+
+- Windows
+ 아래사이트에서 최신 버전을 다운로드 받는다.
+https://kubernetes.io/ko/docs/tasks/tools/install-kubectl-windows/
+
+
+
+ 윈도우에서 kubectl.exe 패스를 추가한다.
+ 환경 변수 창을 띄운다.
+ ```bash
+ Windows 설정 - 시스템 - 정보 - 시스템 정보 - 고급 시스템 설정 - 환경 변수
+ ```
+ 시스템 변수에 path를 선택하고 추가한다.
+
+
+
+- Mac
+```bash
+brew install kubectl
+kubectl version --client
+```
+
+
+
+
+
+Kubernetes는 kubeconfig라는 YAML 파일을 사용하여 kubectl에 대한 클러스터 인증 정보를 저장합니다. kubeconfig에는 명령을 실행할 때 kubectl이 참조하는 컨텍스트 목록이 포함되어 있습니다. 기본적으로 파일은 $HOME/.kube/config에 저장됩니다.
+
+클러스터를 만들면 항목이 환경의 kubeconfig에 자동으로 추가되고 현재 컨텍스트가 해당 클러스터로 변경됩니다.
+
+현재 테스트 과정중에 config 이름이 본인이 설정한 이름으로 구성이 되어 있으니 기존 이름을 config로 변경 합니다.
+
+- config 화일이 여러개인 경우 아래와 같이 병합 하면 됩니다.
+ ```bash
+ export KUBECONFIG=~/.kube/config:~/.kube/config-jakelee
+ ```
+ config가 병합되어 있는지 확인합니다.
+
+ ```bash
+ kubectl config view
+ ```
+
+
+
+
+원하는 k8s를 선택하기 위해 전체 context를 조회 해보고 현재 context를 확인합니다.
+
+```bash
+kubectl config get-contexts
+kubectl config current-context
+```
+
+작업하기 원하는 context ( k8s cluster ) 로 변경 합니다.
+설정을 하면 기본 context로 설정이 됩니다.
+
+```bash
+kubectl config use-context my-cluster-name
+```
+
+
+
+Cluster 정보를 확인 합니다.
+
+```bash
+kubectl cluster-info
+```
+
+
+
+
+
+## Kubectl 활용
+
+
+
+### kubectl 명령어
+
+
+
+자주 사용하는 kubectl 명령어를 알아봅니다.
+
+
+```bash
+# 화일 이름의 리소스를 적용한다. 없으면 insert 있으면 update
+# 아래 create/update 명령어를 대체 할 수 있다.
+kubectl apply -f [화일이름]
+# 화일 이름의 리소스를 생성한다.
+kubectl create -f [화일이름]
+# 화일 이름의 리소스를 update 한다.
+kubectl update -f [화일이름]
+# 화일 이름의 리소스를 delete 한다.
+kubectl delete -f [화일이름]
+
+# 해당 리소스 정보를 보여준다
+kubectl get [리소스 타입] [리소스 이름]
+
+# 해당 리소스 세부 정보를 보여준다
+kubectl describe [리소스 타입] [리소스 이름]
+
+# 해당 리소스 로그 정보를 보여준다
+kubectl log [리소스 이름]
+# 해당 리소스를 삭제 한다
+kubectl delete [리소스 타입] [리소스 이름]
+
+# Pod ( Container ) 안에서 command를 할 수 있다.
+kubectl exec -it [PO 이름] /bin/sh
+```
+
+전체 레이블을 조회한다.
+```bash
+root@jakelee:~# kubectl get pods --show-labels
+NAME READY STATUS RESTARTS AGE LABELS
+flask-edu4-app-74788b6479-qgs2j 1/1 Running 0 24m app=flask-edu4-app,pod-template-hash=74788b6479
+flask-edu4-app-74788b6479-l7gkx 1/1 Running 0 24m app=flask-edu4-app,pod-template-hash=74788b6479
+flask-edu4-app-74788b6479-nmcvv 1/1 Running 0 24m app=flask-edu4-app,pod-template-hash=74788b6479
+flask-edu4-app-74788b6479-f2kcp 1/1 Running 0 24m app=flask-edu4-app,pod-template-hash=74788b6479
+flask-edu4-app-74788b6479-rlght 1/1 Running 0 24m app=flask-edu4-app,pod-template-hash=74788b6479
+```
+
+
+
+### 명령 vs 선언
+
+
+
+명령보다는 선언하여 사용하는 것을 권장.
+
+- 명령
+
+ ```bash
+ kubectl scale --replicas=3 rs/nginx
+ ```
+
+- 선언
+
+ ```yaml
+ apiVersion: apps/v1
+ kind: ReplicaSet
+ metadata:
+ name: frontend
+ labels:
+ app: guestbook
+ tier: frontend
+ spec:
+ # modify replicas according to your case
+ replicas: 3
+ selector:
+ matchLabels:
+ tier: frontend
+ template:
+ metadata:
+ labels:
+ tier: frontend
+ spec:
+ containers:
+ - name: php-redis
+ image: gcr.io/google_samples/gb-frontend:v3
+ ```
+
+ 위의 화일은 에러가 나기 때문에 아래 사이트에서 복사해서 사용한다.
+
+ 참고 : https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/
+
+ ```bash
+ kubectl apply -f [화일명]
+ ```
+
+
+
+
+
+
+
+
+
+## kubernetes 리소스
+
+
+
+### Node
+
+
+
+
+
+`Kubernetes Node` 는 최소한 다음과 같이 동작합니다.
+
+- Kubelet은, 쿠버네티스 마스터와 노드 간 통신을 책임지는 프로세스이며, 하나의 머신 상에서 동작하는 파드와 컨테이너를 관리합니다.
+- (도커, rkt)와 같은 컨테이너 런타임은 레지스트리에서 컨테이너 이미지를 가져와 묶여 있는 것을 풀고 애플리케이션을 동작시키는 책임을 맡습니다.
+
+Node 정보 확인
+```bash
+root@jakelee:~# kubectl get node
+NAME STATUS ROLES AGE VERSION
+jakelee Ready control-plane,master 2d20h v1.22.7+k3s1
+```
+
+Node 상세 정보 확인
+```bash
+root@jakelee:~# kubectl get node -o wide
+NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
+jakelee Ready control-plane,master 2d20h v1.22.7+k3s1 172.27.0.134 Ubuntu 18.04.6 LTS 5.15.0-051500-generic containerd://1.5.9-k3s1
+```
+
+Node 성능 사용량 확인
+```bash
+root@jakelee:~# kubectl top nodes
+NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
+jakelee 151m 1% 7849Mi 49%
+```
+
+추가 명령어
+```bash
+# 해당노드에 pod 를 스케쥴링 하지 않는다.
+kubectl cordon
+
+# 해당노드에 pod 를 스케쥴링 한다.
+kubectl uncordon
+
+# https://arisu1000.tistory.com/27845
+# 노드 관리를 위해서 지정된 노드에 있는 포드들을 다른곳으로 이동시키는 명령입니다.
+kubectl drain
+```
+
+
+
+### Deployment
+
+
+
+
+
+`Deployment`는 Kubernetes 가 애플리케이션의 인스턴스를 어떻게 생성하고 업데이트해야 하는지를 지시합니다.
+`Deployment`가 만들어지면, Kubernetes Master 가 해당 `Deployment` 에 포함된 애플리케이션 인스턴스가 클러스터의 개별 노드에서 실행되도록 스케줄합니다.
+
+
+
+아래 명령어와 같이 수행합니다.
+
+```bash
+kubectl create deployment --image=shclub/edu4:v1 flask-edu4-app
+```
+
+Deployment 가 생성되고 Pod 가 정상적으로 실행중인지 확인합니다.
+
+```bash
+root@jakelee:~# kubectl get all
+NAME READY STATUS RESTARTS AGE
+pod/flask-edu4-app-74788b6479-t6rvt 1/1 Running 0 48s
+
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+service/kubernetes ClusterIP 10.43.0.1 443/TCP 2d17h
+
+NAME READY UP-TO-DATE AVAILABLE AGE
+deployment.apps/flask-edu4-app 1/1 1 1 48s
+
+NAME DESIRED CURRENT READY AGE
+replicaset.apps/flask-edu4-app-74788b6479 1 1 1 48s
+```
+
+
+실행중인 Deployment 를 자세히 확인 해보겠습니다.
+
+```bash
+root@jakelee:~# kubectl describe deployment flask-edu4-app
+Name: flask-edu4-app
+Namespace: default
+CreationTimestamp: Mon, 04 Apr 2022 11:01:34 +0900
+Labels: app=flask-edu4-app
+Annotations: deployment.kubernetes.io/revision: 1
+Selector: app=flask-edu4-app
+Replicas: 1 desired | 1 updated | 1 total | 1 available | 0 unavailable
+StrategyType: RollingUpdate
+MinReadySeconds: 0
+RollingUpdateStrategy: 25% max unavailable, 25% max surge
+Pod Template:
+ Labels: app=flask-edu4-app
+ Containers:
+ edu4:
+ Image: shclub/edu4:v1
+ Port:
+ Host Port:
+ Environment:
+ Mounts:
+ Volumes:
+Conditions:
+ Type Status Reason
+ ---- ------ ------
+ Available True MinimumReplicasAvailable
+ Progressing True NewReplicaSetAvailable
+OldReplicaSets:
+NewReplicaSet: flask-edu4-app-74788b6479 (1/1 replicas created)
+Events:
+ Type Reason Age From Message
+ ---- ------ ---- ---- -------
+ Normal ScalingReplicaSet 2m11s deployment-controller Scaled up replica set flask-edu4-app-74788b6479 to 1
+```
+
+
+
+### Pod
+
+
+
+
+
+
+
+Deployment 가 생성이 되고 나면 Kubernetes 는 여러분의 애플리케이션 인스턴스에 Pod 를 생성했습니다.
+
+Pod 는 하나 또는 그 이상의 애플리케이션 컨테이너 (도커 또는 rkt와 같은)들의 그룹을 나타내는 쿠버네티스의 추상적 개념으로 일부는 컨테이너에 대한 자원을 공유합니다.
+
+
+
+#### Pod 확인
+
+
+
+- pod 정보 확인
+
+ ```bash
+ root@jakelee:~# kubectl get pod
+ NAME READY STATUS RESTARTS AGE
+ flask-edu4-app-74788b6479-t6rvt 1/1 Running 0 6m49s
+ ```
+- pod 성능 사용량 확인
+
+ ```bash
+ root@jakelee:~# kubectl top pod
+ NAME CPU(cores) MEMORY(bytes)
+ flask-edu4-app-74788b6479-t6rvt 1m 17Mi
+ ```
+ 컨테이너 이름까지 같이 보기.
+
+ ```bash
+ root@jakelee:~# kubectl top pod --containers
+ POD NAME CPU(cores) MEMORY(bytes)
+ flask-edu4-app-74788b6479-f2kcp edu4 1m 18Mi
+ ```
+
+
+- Pod 내 Container 의 log 확인
+
+ ```bash
+ root@jakelee:~# kubectl logs flask-edu4-app-74788b6479-t6rvt
+ * Serving Flask app 'app' (lazy loading)
+ * Environment: production
+ WARNING: This is a development server. Do not use it in a production deployment.
+ Use a production WSGI server instead.
+ * Debug mode: off
+ * Running on all addresses (0.0.0.0)
+ WARNING: This is a development server. Do not use it in a production deployment.
+ * Running on http://127.0.0.1:5000
+ * Running on http://10.42.0.24:5000 (Press CTRL+C to quit)
+ ```
+ - 실시간 로그 확인
+
+ ```bash
+ root@jakelee:~# kubectl logs -f flask-edu4-app-74788b6479-t6rvt
+ ```
+
+- Pod 내 Container 에 명령어 수행
+
+ ```bash
+ root@jakelee:~# kubectl exec -it flask-edu4-app-74788b6479-t6rvt /bin/sh
+ ```
+ 하나의 pod에 여러 container가 있는 경우.
+
+ ```bash
+ root@jakelee:~# kubectl top po --containers -n monitoring
+ POD NAME CPU(cores) MEMORY(bytes)
+ alertmanager-prometheus-kube-prometheus-alertmanager-0 alertmanager 1m 21Mi
+ alertmanager-prometheus-kube-prometheus-alertmanager-0 config-reloader 0m 4Mi
+ prometheus-grafana-75898f6f7b-mm6zx grafana 4m 44Mi
+ prometheus-grafana-75898f6f7b-mm6zx grafana-sc-dashboard 3m 61Mi
+ prometheus-grafana-75898f6f7b-mm6zx grafana-sc-datasources 1m 60Mi
+ prometheus-kube-prometheus-operator-85bcb96fcb-ct7pj kube-prometheus-stack 1m 35Mi
+ prometheus-kube-state-metrics-77698656df-82g44 kube-state-metrics 1m 15Mi
+ prometheus-prometheus-kube-prometheus-prometheus-0 config-reloader 0m 7Mi
+ prometheus-prometheus-kube-prometheus-prometheus-0 prometheus 112m 548Mi
+ prometheus-prometheus-node-exporter-5t2jt node-exporter 1m 12Mi
+ ```
+
+ -c 옵션과 컨테이너 이름을 입력하면 해당 컨테이너로 진입한다.
+
+ ```
+ root@jakelee:~# kubectl exec -it prometheus-prometheus-kube-prometheus-prometheus-0 -c prometheus /bin/sh -n monitoring
+ kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
+ /prometheus $
+ ```
+- Pod 상세 정보 확인 - 1
+
+ ```bash
+ root@jakelee:~# kubectl get pod -o wide
+ NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
+ flask-edu4-app-74788b6479-t6rvt 1/1 Running 0 17m 10.42.0.24 jakelee
+ ```
+
+- Pod 상세 정보 확인 - 2
+
+ ```bash
+ root@jakelee:~# kubectl describe pod flask-edu4-app-74788b6479-t6rvt
+ Name: flask-edu4-app-74788b6479-t6rvt
+ Namespace: default
+ Priority: 0
+ Node: jakelee/172.27.0.134
+ Start Time: Mon, 04 Apr 2022 11:01:34 +0900
+ Labels: app=flask-edu4-app
+ pod-template-hash=74788b6479
+ Annotations:
+ Status: Running
+ IP: 10.42.0.24
+ IPs:
+ IP: 10.42.0.24
+ Controlled By: ReplicaSet/flask-edu4-app-74788b6479
+ Containers:
+ edu4:
+ Container ID: containerd://f8d6ebf74ec2b52d2b87141e6e6eeed786b1ce6357ce1c84ab9e1bc76327bc69
+ Image: shclub/edu4:v1
+ Image ID: docker.io/shclub/edu4@sha256:4c89b421e18699420632a98d15659083034e47dc175b5141a5084080b46c9e47
+ Port:
+ Host Port:
+ State: Running
+ Started: Mon, 04 Apr 2022 11:01:41 +0900
+ Ready: True
+ Restart Count: 0
+ Environment:
+ Mounts:
+ /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-jrmkz (ro)
+ Conditions:
+ Type Status
+ Initialized True
+ Ready True
+ ContainersReady True
+ PodScheduled True
+ Volumes:
+ kube-api-access-jrmkz:
+ Type: Projected (a volume that contains injected data from multiple sources)
+ TokenExpirationSeconds: 3607
+ ConfigMapName: kube-root-ca.crt
+ ConfigMapOptional:
+ DownwardAPI: true
+ QoS Class: BestEffort
+ Node-Selectors:
+ Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
+ node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
+ Events:
+ Type Reason Age From Message
+ ---- ------ ---- ---- -------
+ Normal Scheduled 19m default-scheduler Successfully assigned default/flask-edu4-app-74788b6479-t6rvt to jakelee
+ Normal Pulling 19m kubelet Pulling image "shclub/edu4:v1"
+ Normal Pulled 19m kubelet Successfully pulled image "shclub/edu4:v1" in 5.989302707s
+ Normal Created 19m kubelet Created container edu4
+ Normal Started 19m kubelet Started container edu4
+ ```
+
+- 현재 실행중인 Pod 의 정보를 yaml 형식으로 출력
+
+ ```bash
+ root@jakelee:~# kubectl get pod flask-edu4-app-74788b6479-t6rvt -o yaml
+ ```
+
+ 아래와 같이 저장 가능하다.
+
+ ```bash
+ root@jakelee:~# kubectl get pod flask-edu4-app-74788b6479-t6rvt -o yaml > flask-edu4-app.yml
+ ```
+
+
+
+
+#### Pod 생성
+
+
+
+아래 명령어를 통해 Pod 을 생성 할 수 있습니다.
+
+```bash
+root@jakelee:~# kubectl run pod-test-app --image=nginx
+pod/pod-test-app created
+root@jakelee:~# kubectl get po
+NAME READY STATUS RESTARTS AGE
+flask-edu4-app-74788b6479-t6rvt 1/1 Running 0 24m
+pod-test-app 1/1 Running 0 11s
+```
+
+아래와 같이 yaml 을 이용해서 Pod 을 생성 할 수 있습니다.
+
+```bash
+$ cat << EOF | kubectl create -f -
+apiVersion: v1
+kind: Pod
+metadata:
+ labels:
+ run: pod-test-app-2
+ name: pod-test-app-2
+ namespace: default
+spec:
+ containers:
+ - image: nginx
+ name: pod-test-app-2
+EOF
+```
+
+또는
+
+```bash
+kubectl create -f pod-test-app-2.yml
+```
+
+로컬에 화일이 없는 경우 아래와 같이도 가능하다.
+
+https://github.com/shclub/edu4/blob/master/pod-test-app-2.yml 를 로컬에 다운 받은 후 실행
+
+```bash
+kubectl create -f pod-test-app-2.yml
+```
+
+
+
+#### Pod 삭제
+
+
+
+아래 명령어를 통해 Pod 를 삭제 할 수 있습니다.
+
+```bash
+root@jakelee:~# kubectl delete pod pod-test-app
+pod "pod-test-app" deleted
+```
+
+
+
+### Service
+
+
+
+
+
+
+
+kubernetes Pod 들은 언젠가는 죽게됩니다. 실제 Pod 들은 생명주기를 갖습니다.
+
+워커 노드가 죽으면, 노드 상에서 동작하는 Pod 들 또한 종료됩니다.
+
+Kubernetes 에서 service 는 Pod 들에 접근 할 수 있는 정책을 정의하는 추상적 개념입니다.
+
+- ClusterIP (기본값)
+ - 클러스터 내에서 내부 IP 에 대해 서비스를 노출합니다. 이 방식은 클러스터 내에서만 서비스가 접근될 수 있도록 합니다.
+- NodePort
+ - NAT가 이용되는 클러스터 내에서 각각 선택된 노드들의 동일한 포트에 서비스를 노출 시켜줍니다.
+ `:`를 이용하여 클러스터 외부로부터 서비스가 접근할 수 있도록 해줍니다. ClusterIP 의 상위 집합입니다.
+- LoadBalancer
+ - (지원 가능한 경우) 기존 클라우드에서 외부용 로드밸런서를 생성하고 고정된 공인 IP를 할당합니다.
+ NodePort 의 상위 집합입니다.
+- ExternalName
+ - 이름으로 CNAME 레코드를 반환함으로써 임의의 이름(Spec 에서 externalName 으로 명시)을 이용하여 서비스를 노출시켜줍니다.
+ 프록시는 사용되지 않습니다. 이 방식은 kube-dns 버전 1.7 이상에서 지원 가능합니다.
+ - 외부 서비스를 쿠버네티스 내부에서 호출 하고자 할때 사용할 수 있습니다.
+
+
+
+앱을 외부에 노출하기.
+
+아래 명령어를 통해 APP 을 외부에 노출 할 수 있습니다.
+현재 Hands on 환경은 LoadBalancer or Ingress 가 사용이 불가능하므로 Nodeport 를 이용하여 TEST 해보도록 하겠습니다.
+
+expose 옵션 과 create 옵션으로 으로 생성 가능
+
+deployment가 있는지 확인합니다.
+```bash
+root@jakelee:~# kubectl get deployment
+NAME READY UP-TO-DATE AVAILABLE AGE
+flask-edu4-app 1/1 1 1 80m
+```
+
+서비스가 있는지 확인합니다.
+```bash
+root@jakelee:~# kubectl get svc
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+kubernetes ClusterIP 10.43.0.1 443/TCP 2d19h
+```
+APP를 노출합니다. 포트는 컨테이너 포트이고 flask 소스에 5000 번으로 설정.
+- 참고 : https://github.com/shclub/edu4/blob/master/app.py
+
+```bash
+root@jakelee:~# kubectl expose deployment flask-edu4-app --port 5000
+service/flask-edu4-app exposed
+```
+또는
+
+Create 옵션으로 생성 방법
+```bash
+root@jakelee:~# kubectl create service clusterip flask-edu4-app --tcp=5000
+service/flask-edu4-app exposed
+```
+
+서비스를 조회해 보면 80번 포트로 접속할 수 있는 서비스가 생성되었습니다.
+```bash
+root@jakelee:~# kubectl get svc
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+kubernetes ClusterIP 10.43.0.1 443/TCP 2d19h
+flask-edu4-app ClusterIP 10.43.119.5 5000/TCP 3s
+```
+
+
+
+### 잠시 Route 설정 시작
+
+
+
+오픈 쉬프트에서는 ingress 대신 route를 통해서 외부에서 접근 할수 있게 한다.
+아래처럼 oc 명령어를 사용하여 expose하면 외부에서 접근 할 수 있는 url 이 생긴다.
+
+```bash
+root@jakelee:~# oc expose svc/flask-edu4-app --name flask-edu
+```
+
+route 조회해 본다.
+
+```bash
+root@jakelee:~# kubectl get route
+```
+
+
+
+### 잠시 Route 설정 끝
+
+
+
+위와 명령으로 생성하는 경우, 기본적으로 ClusterIP 로 생성이 됩니다.
+
+아래 명령을 통해 ClusterIP 를 NodePort 로 변경하도록 하겠습니다.
+
+```bash
+root@jakelee:~# kubectl edit service flask-edu4-app
+...
+spec:
+ clusterIP: 10.43.119.5
+ clusterIPs:
+ - 10.43.119.5
+ internalTrafficPolicy: Cluster
+ ipFamilies:
+ - IPv4
+ ipFamilyPolicy: SingleStack
+ ports:
+ - port: 80
+ protocol: TCP
+ targetPort: 80
+ selector:
+ app: flask-edu4-app
+ sessionAffinity: None
+ type: ClusterIP <<<
+ ...
+ ```
+
+위와 같이 ClusterIP 부분은 NodePort 로 변경하고 저장을 합니다.
+(저장 및 종료, VI 과 동일함.)
+
+NodePort 로 변경이 되었는지 확인합니다.
+
+포트를 명시하지 않으면 아래와 같이 30000 ~ 32767 범위내에서 자동 할당한다.
+
+```bash
+root@jakelee:~# kubectl edit svc flask-edu4-app
+service/flask-edu4-app edited
+root@jakelee:~# kubectl get svc
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+kubernetes ClusterIP 10.43.0.1 443/TCP 2d19h
+flask-edu4-app NodePort 10.43.119.5 5000:30685/TCP 9m7s
+```
+
+위와 같이 30685 로 Port 할당된 것을 확인 할 수 있습니다.
+(해당 Port 할당은 따로 yaml 에서 지정을 안하면 랜덤 Mapping 입니다.)
+
+수동할당시에는 포트를 명시한다. (chapter4 참고 )
+
+
+
+서비스를 테스트 해봅니다. 아래와 같이 설정.
+
+```bash
+curl :<할당된 노드 포트 >
+```
+
+```bash
+root@jakelee:~# curl 211.34.231.84:30685
+ Container EDU | POD Working : flask-edu4-app-74788b6479-t6rvt | v=1
+```
+
+컨테이너 IP로도 테스트 할 수 있다. 포트는 컨테이너 포트 사용.
+```bash
+root@jakelee:~# curl 211.34.231.84:5000
+ Container EDU | POD Working : flask-edu4-app-74788b6479-t6rvt | v=1
+```
+
+웹브라우저에서도 테스트 할 수 있다.
+
+
+
+
+
+
+### Scale Out
+
+
+
+Pod 을 Scale-out 하는 방법을 실습한다.
+
+- Before
+
+- After
+
+
+
+Command 를 이용하여 Scale-out 를 한다.
+기존에 1개의 Pod 으로 실행중이던 APP 을 5개의 Pod 으로 Scale-out 하도록 한다.
+
+현재 deployment 확인
+```bash
+root@jakelee:~# kubectl get deployment
+NAME READY UP-TO-DATE AVAILABLE AGE
+flask-edu4-app 1/1 1 1 123m
+```
+Scale-out 를 한다.
+```bash
+root@jakelee:~# kubectl scale deployment --replicas=5 flask-edu4-app
+deployment.apps/flask-edu4-app scaled
+```
+
+Scale-out 된 deployment를 확인한다.
+```bash
+root@jakelee:~# kubectl get deployment
+NAME READY UP-TO-DATE AVAILABLE AGE
+flask-edu4-app 5/5 5 5 123m
+```
+
+POD가 5개로 늘어난것을 확인할 수 있고 신규로 4개가 생성되었다.
+```bash
+root@jakelee:~# kubectl get po
+NAME READY STATUS RESTARTS AGE
+flask-edu4-app-74788b6479-t6rvt 1/1 Running 0 126m
+flask-edu4-app-74788b6479-l59sp 1/1 Running 0 2m30s
+flask-edu4-app-74788b6479-4krcs 1/1 Running 0 2m30s
+flask-edu4-app-74788b6479-gmvtk 1/1 Running 0 2m30s
+flask-edu4-app-74788b6479-g9j8x 1/1 Running 0 2m30s
+```
+
+서비스 확인(Round-Robin)이 되는지 아래 명령어를 사용하여 확인한다.
+TEST 환경에 맞게 수정합니다.
+
+```bash
+while true; do curl <본인 VM Public IP>:<할당된 노드포트>; done
+```
+
+아래와 같이 Scale-out 되어 서비스 중인 것을 볼 수 있습니다.
+
+```bash
+root@jakelee:~# while true; do curl 210.106.105.165:30685; done
+ Container EDU | POD Working : flask-edu4-app-74788b6479-l59sp | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-gmvtk | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-4krcs | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-gmvtk | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-gmvtk | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-gmvtk | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-l59sp | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-g9j8x | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-4krcs | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-t6rvt | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-gmvtk | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-g9j8x | v=1
+```
+
+
+
+
+## 배포 ( Rolling Update / Rollback )
+
+
+
+### Update APP
+
+
+
+Rolling Update / Rollback APP 에 대한 방법을 실습한다.
+
+https://github.com/shclub/edu4 의 저장소의 file을 update 한다.
+
+- python app.py의 소스를 Update 합니다.
+
+- Jenkins 의 소스를 Update 합니다. 리포지토리는 edu4로 이미 변경
+
+
+
+Jenkins 로 빌드 하여 새로운 버전의 도커이미지를 생성합니다.
+v2버전이 생성된것을 확인 할 수 있다.
+
+
+
+시간 관계상 Push 된 이미지를 사용할 것입니다.
+
+Container Image Tag : shclub/edu4:v2
+
+
+
+
+### Rolling Update
+
+
+
+아래 명령을 통해 기존에 v1 에서 v2 로 image 를 변경하여 Rolling Update 해보겠습니다.
+
+- 기존정보
+
+```bash
+root@jakelee:~# kubectl describe deployments flask-edu4-app
+Name: flask-edu4-app
+Namespace: default
+CreationTimestamp: Mon, 04 Apr 2022 11:01:34 +0900
+Labels: app=flask-edu4-app
+Annotations: deployment.kubernetes.io/revision: 1
+Selector: app=flask-edu4-app
+Replicas: 5 desired | 5 updated | 5 total | 5 available | 0 unavailable
+StrategyType: RollingUpdate
+MinReadySeconds: 0
+RollingUpdateStrategy: 25% max unavailable, 25% max surge
+Pod Template:
+ Labels: app=flask-edu4-app
+ Containers:
+ edu4:
+ Image: shclub/edu4:v1
+ Port:
+ Host Port:
+ Environment:
+ Mounts:
+ Volumes:
+Conditions:
+ Type Status Reason
+ ---- ------ ------
+ Progressing True NewReplicaSetAvailable
+ Available True MinimumReplicasAvailable
+OldReplicaSets:
+NewReplicaSet: flask-edu4-app-74788b6479 (5/5 replicas created)
+Events:
+ Type Reason Age From Message
+ ---- ------ ---- ---- -------
+ Normal ScalingReplicaSet 23m (x2 over 80m) deployment-controller Scaled up replica set flask-edu4-app-74788b6479 to 5
+```
+
+- 이미지를 변경합니다. 켠테이너 이름은 describe 에서 containers 밑에 있는 이름 이고 여기에서는 edu4 이다.
+
+```bash
+kubectl set image deployments <컨테이너이름>=<변경할 이미지>
+```
+
+```bash
+root@jakelee:~# kubectl set image deployments flask-edu4-app edu4=shclub/edu4:v2
+deployment.apps/flask-edu4-app image updated
+```
+
+- Rolling Update 상태 확인
+ - 상태 확인
+ ```bash
+ root@jakelee:~# kubectl rollout status deployments flask-edu4-app
+ Waiting for deployment "flask-edu4-app" rollout to finish: 1 old replicas are pending termination...
+ Waiting for deployment "flask-edu4-app" rollout to finish: 1 old replicas are pending termination...
+ Waiting for deployment "flask-edu4-app" rollout to finish: 1 old replicas are pending termination...
+ deployment "flask-edu4-app" successfully rolled out
+ ```
+ - 실제 서비스 확인 : v2로 바뀐것을 확인 할 수 있다.
+ ```bash
+ root@jakelee:~# while true; do curl 210.106.105.165:30685; done
+ Container EDU | POD Working : flask-edu4-app-757bcc87db-ft9k9 | v=2
+ Container EDU | POD Working : flask-edu4-app-757bcc87db-sns6z | v=2
+ Container EDU | POD Working : flask-edu4-app-757bcc87db-l69b8 | v=2
+ Container EDU | POD Working : flask-edu4-app-757bcc87db-x9fvn | v=2
+ Container EDU | POD Working : flask-edu4-app-757bcc87db-l69b8 | v=2
+ Container EDU | POD Working : flask-edu4-app-757bcc87db-ft9k9 | v=2
+ Container EDU | POD Working : flask-edu4-app-757bcc87db-sns6z | v=2
+ ```
+
+- 변경 확인
+
+```bash
+root@jakelee:~# kubectl describe deployments flask-edu4-app
+Name: flask-edu4-app
+Namespace: default
+CreationTimestamp: Mon, 04 Apr 2022 11:01:34 +0900
+Labels: app=flask-edu4-app
+Annotations: deployment.kubernetes.io/revision: 2
+Selector: app=flask-edu4-app
+Replicas: 5 desired | 5 updated | 5 total | 5 available | 0 unavailable
+StrategyType: RollingUpdate
+MinReadySeconds: 0
+RollingUpdateStrategy: 25% max unavailable, 25% max surge
+Pod Template:
+ Labels: app=flask-edu4-app
+ Containers:
+ edu4:
+ Image: shclub/edu4:v2
+ Port:
+ Host Port:
+ Environment:
+ Mounts:
+ Volumes:
+Conditions:
+ Type Status Reason
+ ---- ------ ------
+ Available True MinimumReplicasAvailable
+ Progressing True NewReplicaSetAvailable
+OldReplicaSets:
+NewReplicaSet: flask-edu4-app-757bcc87db (5/5 replicas created)
+Events:
+ Type Reason Age From Message
+ ---- ------ ---- ---- -------
+ Normal ScalingReplicaSet 34m (x2 over 91m) deployment-controller Scaled up replica set flask-edu4-app-74788b6479 to 5
+ Normal ScalingReplicaSet 7m12s deployment-controller Scaled up replica set flask-edu4-app-757bcc87db to 2
+ Normal ScalingReplicaSet 7m12s deployment-controller Scaled down replica set flask-edu4-app-74788b6479 to 4
+ Normal ScalingReplicaSet 7m11s deployment-controller Scaled up replica set flask-edu4-app-757bcc87db to 3
+ Normal ScalingReplicaSet 7m2s deployment-controller Scaled down replica set flask-edu4-app-74788b6479 to 3
+ Normal ScalingReplicaSet 7m2s deployment-controller Scaled up replica set flask-edu4-app-757bcc87db to 4
+ Normal ScalingReplicaSet 7m2s (x2 over 78m) deployment-controller Scaled down replica set flask-edu4-app-74788b6479 to 1
+ Normal ScalingReplicaSet 7m2s deployment-controller Scaled up replica set flask-edu4-app-757bcc87db to 5
+ Normal ScalingReplicaSet 7m1s deployment-controller Scaled down replica set flask-edu4-app-74788b6479 to 0
+ ```
+
+
+
+
+### Rollback
+
+
+
+Rollback 은 배포된 APP 에 문제가 있을 때, 다시 이전 이미지로 배포 해야 되는 경우 사용하는 방법입니다.
+(꼭 문제가 있어야 사용이 가능한 것은 아님, 주 목적은 이전 버전으로 Rollback 하기 위함입니다.)
+
+
+- Rollback 진행
+ - 현재 상태 확인 : 2개의 Revision 이 있고 2번이 현재 버전이다.
+ ```bash
+ root@jakelee:~# kubectl rollout history deployment flask-edu4-app
+ deployment.apps/flask-edu4-app
+ REVISION CHANGE-CAUSE
+ 1
+ 2
+ ```
+ - Rollback 진행 : 바로 이전 버전으로 진행이 된다.
+ ```bash
+ root@jakelee:~# kubectl rollout undo deployment flask-edu4-app
+ deployment.apps/flask-edu4-app rolled back
+ ```
+ - Rollback 확인 : 이미지는 shclub/edu4:v1 으로 변경이 되면 Revision은 3으로 올라간다.
+ ```bash
+ root@jakelee:~# kubectl describe deployments.apps flask-edu4-app
+ Name: flask-edu4-app
+ Namespace: default
+ CreationTimestamp: Mon, 04 Apr 2022 11:01:34 +0900
+ Labels: app=flask-edu4-app
+ Annotations: deployment.kubernetes.io/revision: 3
+ Selector: app=flask-edu4-app
+ Replicas: 5 desired | 5 updated | 5 total | 5 available | 0 unavailable
+ StrategyType: RollingUpdate
+ MinReadySeconds: 0
+ RollingUpdateStrategy: 25% max unavailable, 25% max surge
+ Pod Template:
+ Labels: app=flask-edu4-app
+ Containers:
+ edu4:
+ Image: shclub/edu4:v1
+ Port:
+ Host Port:
+ Environment:
+ Mounts:
+ Volumes:
+ Conditions:
+ Type Status Reason
+ ---- ------ ------
+ Available True MinimumReplicasAvailable
+ Progressing True NewReplicaSetAvailable
+ OldReplicaSets:
+ NewReplicaSet: flask-edu4-app-74788b6479 (5/5 replicas created)
+ Events:
+ Type Reason Age From Message
+ ---- ------ ---- ---- -------
+ Normal ScalingReplicaSet 14m deployment-controller Scaled down replica set flask-edu4-app-74788b6479 to 4
+ Normal ScalingReplicaSet 14m deployment-controller Scaled up replica set flask-edu4-app-757bcc87db to 2
+ Normal ScalingReplicaSet 14m deployment-controller Scaled up replica set flask-edu4-app-757bcc87db to 3
+ Normal ScalingReplicaSet 13m deployment-controller Scaled up replica set flask-edu4-app-757bcc87db to 5
+ Normal ScalingReplicaSet 13m deployment-controller Scaled down replica set flask-edu4-app-74788b6479 to 3
+ Normal ScalingReplicaSet 13m deployment-controller Scaled up replica set flask-edu4-app-757bcc87db to 4
+ Normal ScalingReplicaSet 13m (x2 over 85m) deployment-controller Scaled down replica set flask-edu4-app-74788b6479 to 1
+ Normal ScalingReplicaSet 13m deployment-controller Scaled down replica set flask-edu4-app-74788b6479 to 0
+ Normal ScalingReplicaSet 14s deployment-controller Scaled up replica set flask-edu4-app-74788b6479 to 3
+ Normal ScalingReplicaSet 14s deployment-controller Scaled down replica set flask-edu4-app-757bcc87db to 4
+ Normal ScalingReplicaSet 14s deployment-controller Scaled up replica set flask-edu4-app-74788b6479 to 2
+ Normal ScalingReplicaSet 13s deployment-controller Scaled down replica set flask-edu4-app-757bcc87db to 3
+ Normal ScalingReplicaSet 13s deployment-controller Scaled up replica set flask-edu4-app-74788b6479 to 4
+ Normal ScalingReplicaSet 13s deployment-controller Scaled down replica set flask-edu4-app-757bcc87db to 2
+ Normal ScalingReplicaSet 13s (x3 over 98m) deployment-controller Scaled up replica set flask-edu4-app-74788b6479 to 5
+ Normal ScalingReplicaSet 12s deployment-controller Scaled down replica set flask-edu4-app-757bcc87db to 1
+ Normal ScalingReplicaSet 10s deployment-controller Scaled down replica set flask-edu4-app-757bcc87db to 0
+ ```
+
+ - Rollback 완료 : v1으로 서비스가 변경이 되었다.
+ ```bash
+ root@jakelee:~# while true; do curl 210.106.105.165:30685; done
+ Container EDU | POD Working : flask-edu4-app-74788b6479-qgs2j | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-qgs2j | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-qgs2j | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-qgs2j | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-f2kcp | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-f2kcp | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-l7gkx | v=1
+ Container EDU | POD Working : flask-edu4-app-74788b6479-l7gkx | v=1
+ ```
+
+ - 특정 revision 으로 변경하는 방법
+ ```bash
+ kubectl rollout undo deployment flask-edu4-app --to-revision=1
+ ```
+
+
+
+## Serivce Expose ( Ingress / Route)
+
+
+
+### Ingress / Route
+
+
+
+
+Ingress는 도메인으로 서비스를 접속하기위해 필요한 오브젝트입니다.
+
+하나의 클러스터에서 여러 가지 서비스를 운영한다면 외부 연결을 어떻게 할까요? NodePort를 이용하면 서비스 개수만큼 포트를 오픈하고 사용자에게 어떤 포트인지 알려줘야 합니다. 그럴순 없죠!
+
+
+
+
+
+위 샘플은 example.com, subicura.com/blog, subicura.com/help 주소로 서로 다른 서비스에 접근하는 모습입니다.
+
+80(http) 또는 443(https) 포트로 여러 개의 서비스를 연결해야 하는데 이럴 때 Ingress를 사용합니다.
+
+- wildcard DNS
+ip를 기반으로 도메인을 쉽게 사용할 수 있습니다. 실습에서 사용합니다.
+
+ - sslip.io
+ - xip.io
+ - nip.io
+
+ ```bash
+ 10.0.0.1.nip.io maps to 10.0.0.1
+ 192-168-1-250.nip.io maps to 192.168.1.250
+ app.10.8.0.1.nip.io maps to 10.8.0.1
+ app-37-247-48-68.nip.io maps to 37.247.48.68
+ customer1.app.10.0.0.1.nip.io maps to 10.0.0.1
+ customer2-app-127-0-0-1.nip.io maps to 127.0.0.1
+ ```
+
+Ingress Nginx 를 설치한다.
+
+```bash
+root@jakelee:~# kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.0/deploy/static/provider/baremetal/deploy.yaml
+```
+
+설치가 완료되면 ingress-nginx namespace가 생기고 서비스는 NodePort로 아래와 같이 자동으로 설정이 되어 있다.
+
+- http : 31996 , https : 31023
+
+```bash
+root@jakelee:~# kubectl get all -n ingress-nginx
+NAME READY STATUS RESTARTS AGE
+pod/ingress-nginx-admission-create--1-w9z7x 0/1 Completed 0 11m
+pod/ingress-nginx-admission-patch--1-v9v66 0/1 Completed 1 11m
+pod/ingress-nginx-controller-8cf5559f8-nc96z 1/1 Running 0 11m
+
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+service/ingress-nginx-controller-admission ClusterIP 10.43.52.204 443/TCP 11m
+service/ingress-nginx-controller NodePort 10.43.27.14 80:31996/TCP,443:31023/TCP 11m
+
+NAME READY UP-TO-DATE AVAILABLE AGE
+deployment.apps/ingress-nginx-controller 1/1 1 1 11m
+
+NAME DESIRED CURRENT READY AGE
+replicaset.apps/ingress-nginx-controller-8cf5559f8 1 1 1 11m
+
+NAME COMPLETIONS DURATION AGE
+job.batch/ingress-nginx-admission-create 1/1 6s 11m
+job.batch/ingress-nginx-admission-patch 1/1 7s 11m
+```
+
+서비스와 포트를 확인하고 정상 작동하는지 확인한다.
+```bash
+root@jakelee:~# kubectl get svc -n ingress-nginx
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+ingress-nginx-controller-admission ClusterIP 10.43.52.204 443/TCP 147m
+ingress-nginx-controller NodePort 10.43.27.14 80:31996/TCP,443:31023/TCP 147m
+
+root@jakelee:~# curl -I http://127.0.0.1:31996/healthz
+HTTP/1.1 200 OK
+Date: Mon, 04 Apr 2022 09:07:43 GMT
+Content-Type: text/html
+Content-Length: 0
+Connection: keep-alive
+
+# 본인 VM Public IP 로도 확인 가능하다.
+root@jakelee:~# curl -I http://210.106.105.165:31996/healthz
+HTTP/1.1 200 OK
+Date: Mon, 04 Apr 2022 09:14:52 GMT
+Content-Type: text/html
+Content-Length: 0
+Connection: keep-alive
+
+# 본인 VM 도메인 으로도 확인 가능하다.
+root@jakelee:~# curl -I http://210.106.105.165.nip.io:31996/healthz
+HTTP/1.1 200 OK
+Date: Mon, 04 Apr 2022 09:46:17 GMT
+Content-Type: text/html
+Content-Length: 0
+Connection: keep-alive
+```
+
+서비스를 도메인으로 접속하기 위해서 ingress를 설정한다.
+해당 화일은 https://github.com/shclub/edu4/blob/master/ingress_sample1.yaml 에서 다운 받는다.
+
+backend.service.port.number는 해당 서비스의 컨테이너 포트를 명시한다.
+
+
+```yaml
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: nginx-ingress
+ annotations:
+ kubernetes.io/ingress.class: "nginx"
+ ingress.kubernetes.io/rewrite-target: /
+ ingressclass.kubernetes.io/is-default-class: "true"
+spec:
+ rules:
+ - host: 210.106.105.165.nip.io
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: flask-edu4-app
+ port:
+ number: 5000
+```
+
+```bash
+root@jakelee:~# kubectl apply -f ingress-sample1.yaml
+ingress.networking.k8s.io/nginx-ingress created
+root@jakelee:~# kubectl get ing
+NAME CLASS HOSTS ADDRESS PORTS AGE
+nginx-ingress nginx 210.106.105.165.nip.io 172.27.0.134 80 12m
+```
+
+ingress를 통하여 서비스를 접속하여 봅니다.
+
+```bash
+root@jakelee:~# curl http://210.106.105.165.nip.io:31996/
+ Container EDU | POD Working : flask-edu4-app-74788b6479-rlght | v=1
+```
+
+웹으로도 서비스를 접속하여 봅니다.
+
+
+
+
+
+ingressclass를 ingress 마다 넣어주는 불편은 아래와 같이 변경하면 ingress yaml 생성시 annotation 에서 삭제 가능하다.
+
+```bash
+root@jakelee:~# kubectl get ingressclasses --namespace=ingress-nginx
+NAME CONTROLLER PARAMETERS AGE
+nginx k8s.io/ingress-nginx 3h35m
+root@jakelee:~# kubectl edit ingressclasses nginx --namespace=ingress-nginx
+```
+
+아래와 같이 ingressclass를 추가한다.
+
+```bash
+metadata:
+ annotations:
+ ingressclass.kubernetes.io/is-default-class: "true" <<추가
+```
+
+
+
+
+
+### Ingress - Traefik
+
+
+
+
+k3s 에는 default 로 Traefik 이라는 Ingress Controller가 설치가 되어 있다.
+
+Traefik을 사용하여 80/443 포트로 접속하는 방법에 대해서 실습을 한다.
+
+traefik pod를 확인한다.
+
+```bash
+root@jakelee:~# kubectl get po -n kube-system
+NAME READY STATUS RESTARTS AGE
+local-path-provisioner-84bb864455-pkktg 1/1 Running 0 9d
+helm-install-traefik-crd--1-2l5b9 0/1 Completed 0 9d
+svclb-traefik-xst65 2/2 Running 1 (9d ago) 9d
+coredns-96cc4f57d-f87gg 1/1 Running 0 9d
+metrics-server-ff9dbcb6c-h9tv2 1/1 Running 0 9d
+helm-install-traefik--1-5g9lq 0/1 Completed 0 36m
+traefik-747c4ffbd6-q6hpd 1/1 Running 0 36m
+```
+
+traefik service를 확인한다.
+
+```bash
+root@jakelee:~# kubectl get svc traefik -n kube-system
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+traefik LoadBalancer 10.43.167.187 172.27.0.134 80:30622/TCP,443:31534/TCP 9d
+```
+
+Ingress를 생성하기 전에 inspekt라는 pod를 inspect하는 서비스를 생성한다.
+
+```bash
+root@jakelee:~# kubectl apply -k github.com/shclub/inspekt
+```
+
+Pod와 서비스가 생성된 것을 확인한다.
+
+
+
+ingress를 생성하기 위해 vi 에디터로 inspekt_ingress.yaml 화일을 생성한다.
+- 여기에서도 다운 가능 : https://github.com/shclub/edu4/blob/master/inspekt_ingress.yaml
+
+아래에 kubernetes.io/ingress.class: traefik 의미는 traefik의 ingress controller를 사용하겠다는 의미이다.
+
+서비스 포트는 컨테이너 포트이다.
+
+```bash
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: inspekt-traefik-ingress
+ annotations:
+ ingress.kubernetes.io/rewrite-target: "/"
+ kubernetes.io/ingress.class: traefik
+spec:
+ rules:
+ - host: inspekt.210.106.105.165.sslip.io
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: inspekt
+ port:
+ number: 80
+
+```
+
+해당 화일을 적용하고 ingress가 신규로 생성된 것을 확인 할 수 있다.
+
+```bash
+root@jakelee:~# kubectl apply -f inspekt_ingress.yaml
+ingress.networking.k8s.io/inspekt-traefik-ingress created
+root@jakelee:~# kubectl get ing
+NAME CLASS HOSTS ADDRESS PORTS AGE
+flask-edu4-app-v1 nginx 210.106.105.165.nip.io 172.27.0.134 80 3d12h
+inspekt-traefik-ingress inspekt.210.106.105.165.sslip.io 172.27.0.134 80 5s
+```
+
+hosts 의 값을 복사하여 web browser 에서 실행한다.
+80번 포트로 접속이 된것을 확인 할 수 있다.
+
+
+
+
+
+http (80) 포트인 경우는 위와 같이 가능하지만 https (443) 인 경우는 서비스 마다 인증서를 만들어 추가 해야한다.
+
+우리는 교육용이기 때문에 ssl을 적용하지 않고 테스트를 진행한다.
+
+/var/lib/rancher/k3s/server/manifests/traefik.yaml 화일을 수정한다.
+
+```bash
+root@jakelee:~# vi /var/lib/rancher/k3s/server/manifests/traefik.yaml
+```
+
+valuesContent 밑에 2개의 라인을 추가한다. ssl verify를 skip 한다는 의미이다.
+
+```bash
+valuesContent: |-
+ globalArguments: << 추가
+ - "--serversTransport.insecureSkipVerify=true" << 추가
+```
+
+이제 https(443) 포트 케이스는 ingress로 생성할 준비가 되어 있다.
+
+ingress를 생성하기 위해 vi 에디터로 argocd_ingress.yaml 화일을 생성한다.
+- 여기에서도 다운 가능 : https://github.com/shclub/edu4/blob/master/argocd_ingress.yaml
+
+argocd는 4주차에 설치 실습이 있어 4주차 설치 이후 아래 내용을 따라 하면 된다.
+
+argocd 서비스를 확인힌다.
+
+```bash
+root@jakelee:~# kubectl get svc -n argocd
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+argocd-applicationset-controller ClusterIP 10.43.26.65 7000/TCP 9d
+argocd-dex-server ClusterIP 10.43.239.221 5556/TCP,5557/TCP,5558/TCP 9d
+argocd-metrics ClusterIP 10.43.251.44 8082/TCP 9d
+argocd-notifications-controller-metrics ClusterIP 10.43.214.197 9001/TCP 9d
+argocd-redis ClusterIP 10.43.12.131 6379/TCP 9d
+argocd-repo-server ClusterIP 10.43.132.197 8081/TCP,8084/TCP 9d
+argocd-server-metrics ClusterIP 10.43.200.82 8083/TCP 9d
+argocd-server NodePort 10.43.247.167 80:30000/TCP,443:30001/TCP 9d
+```
+
+우리가 ingress 설정을 할 서비스는 argocd-server 이다.
+
+
+```bash
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: argocd-ingress
+ annotations:
+ kubernetes.io/ingress.class: traefik
+ ingress.kubernetes.io/ssl-redirect: "true"
+ ingress.kubernetes.io/force-ssl-redirect: "true"
+ ingress.kubernetes.io/ssl-passthrough: "true"
+spec:
+ rules:
+ - host: argocd.210.106.105.165.sslip.io
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: argocd-server
+ port:
+ number: 80
+```
+
+argocd namespace에 ingress를 적용하고 ingress를 확인한다.
+
+```bash
+oot@jakelee:~# kubectl apply -f argocd_ingress.yaml -n argocd
+ingress.networking.k8s.io/argocd-ingress unchanged
+root@jakelee:~# kubectl get ing -n argocd
+NAME CLASS HOSTS ADDRESS PORTS AGE
+argocd-ingress argocd.210.106.105.165.sslip.io 172.27.0.134 80 65m
+```
+
+hosts 의 값을 복사하여 web browser 에서 실행한다.
+https (443) 로 접속이 된것을 확인 할 수 있다.
+
+
+
+
+
+Too many redirect 오류가 발생하거나 로그인이 안되는 경우는 argocd에서 직접
+인증서를 처리하려고 하기 때문에 에러가 발생한다.
+deployment에 insecure 옵션을 추가 하면 에러가 발생하지 않는다.
+
+```bash
+root@jakelee:~# kubectl get deploy -n argocd
+NAME READY UP-TO-DATE AVAILABLE AGE
+argocd-redis 1/1 1 1 9d
+argocd-applicationset-controller 1/1 1 1 9d
+argocd-notifications-controller 1/1 1 1 9d
+argocd-dex-server 1/1 1 1 9d
+argocd-repo-server 1/1 1 1 9d
+inspekt-deployment 1/1 1 1 13h
+argocd-server 1/1 1 1 9d
+root@jakelee:~# kubectl edit deploy argocd-server -n argocd
+```
+
+command 의 argocd-server 밑에 -- insecure를 추가하고 저장하고 나온다.
+
+argocd-server pod가 자동으로 재 실행 된다.
+
+```bash
+ containers:
+ - command:
+ - argocd-server
+ - --insecure << 추가
+ env:
+```
+
+이제 웹에서 다시 로그인을 수행한다.
+
+
+
+### Horizontal Pod Autoscaler (hpa)
+
+
+
+
+이 실습을 진행하기 위해서는 terminal이 2개 이상 열려 있어야 하며 metric server 가 설치가 되어야 합니다.
+
+Metric 서버는 api를 통해서 컨테이너 CPU 및 메모리 사용량과 같은 리소스 사용량 메트릭을 제공하는데요, hpa는 이 api를 호출해서 Metric 서버에서 제공해주는 리소스 사용량을 기준으로 scaling 여부를 판단합니다.
+
+k3s는 metric server가 설치가 이미되어 있습니다.
+
+설치 방법
+- Metric-server github에서 제공하는 config 파일을 이용해 간단하게 설치할 수 있습니다. 필요하다면 파일을 내려받은 뒤 config 파일을 수정하고 배포하면 됩니다.
+ ```bash
+ kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.4.1/components.yaml
+ ```
+
+
+
+예제 참고 : https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/horizontal-pod-autoscaler.html
+
+
+
+현재 Node의 CPU 사용률을 확인합니다.
+```bash
+root@jakelee:~# kubectl top nodes
+NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
+jakelee 488m 6% 8274Mi 51%
+```
+테스트용 php 소스 이미지를 배포합니다. 이 Apache 웹 서버 파드에는 500 millicpu CPU 제한이 지정되며 포트 80에서 제공됩니다.
+현재 서버는 CPU : 8core
+- requests : 200m ( 초기 설정 )
+- limits : 500m (제한 )
+
+```yaml
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: php-apache
+spec:
+ selector:
+ matchLabels:
+ run: php-apache
+ replicas: 1
+ template:
+ metadata:
+ labels:
+ run: php-apache
+ spec:
+ containers:
+ - name: php-apache
+ image: k8s.gcr.io/hpa-example
+ ports:
+ - containerPort: 80
+ resources:
+ limits:
+ cpu: 500m
+ requests:
+ cpu: 200m
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: php-apache
+ labels:
+ run: php-apache
+spec:
+ ports:
+ - port: 80
+ selector:
+ run: php-apache
+```
+
+배포시작.
+
+```bash
+root@jakelee:~# kubectl apply -f https://k8s.io/examples/application/php-apache.yaml
+deployment.apps/php-apache created
+service/php-apache created
+```
+
+Deployment와 service가 생성이 되었고 php-apache 배포를 위해 Horizontal Pod Autoscaler 리소스를 생성합니다.
+
+이 명령을 통해 최소 1개에서 최대 10개의 POD가 배포에 대해 10퍼센트의 CPU 사용률을 달성하려는 자동 조정기가 생성됩니다.
+평균 CPU 로드가 10퍼센트 이하인 경우 Autoscaler는 Pod 의 수를 최소 1개로 줄이려고 합니다.
+로드가 50퍼센트보다 큰 경우 포드의 수를 최대 10개로 늘이려고 합니다.
+
+우리는 빠른 테스트를 위해 10% 로 설정하고 테스트를 한다.
+터미널 창에서 아래 명령어를 실행한다.
+
+```bash
+root@jakelee:~# kubectl autoscale deployment php-apache --cpu-percent=10 --min=1 --max=10
+horizontalpodautoscaler.autoscaling/php-apache autoscaled
+root@jakelee:~# kubectl get hpa php-apache
+NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
+php-apache Deployment/php-apache /10% 1 10 0 11s
+```
+
+실시간으로 hpa 상태를 모니터링 한다. 초기에는 Replica 가 minumum 1 로 설정됩니다.
+
+```bash
+root@jakelee:~# kubectl get hpa php-apache -w
+NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
+php-apache Deployment/php-apache 0%/10% 1 10 1 15s
+```
+
+
+
+새로운 터미널에서 아래와 같이 부하를 줍니다.
+
+```bash
+root@jakelee:~# kubectl run -i --tty load-generator --rm --image=busybox --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"
+```
+
+아래에 ok가 나오면 부하가 들어간다는 의미.
+
+```bash
+If you don't see a command prompt, try pressing enter.
+OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!
+```
+
+이전 터미널에서 계속 적으로 모니터링 합니다. 부하에 따라 Replica 가 증가하고 최대값인 10으로 증가 된것을 확인 할수 있습니다.
+
+```bash
+root@jakelee:~# kubectl get hpa php-apache -w
+NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
+php-apache Deployment/php-apache 0%/10% 1 10 1 15s
+php-apache Deployment/php-apache 141%/10% 1 10 1 60s
+php-apache Deployment/php-apache 253%/10% 1 10 4 75s
+php-apache Deployment/php-apache 236%/10% 1 10 8 90s
+```
+
+부하를 주는 화면에서 ctrl+c 를 눌러 부하를 중단합니다.
+최소값으로 돌아오는데는 5분 이상이 소요가 됩니다.
+
+```bash
+root@jakelee:~# kubectl get hpa php-apache -w
+NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
+php-apache Deployment/php-apache 0%/10% 1 10 1 15s
+php-apache Deployment/php-apache 141%/10% 1 10 1 60s
+php-apache Deployment/php-apache 253%/10% 1 10 4 75s
+php-apache Deployment/php-apache 236%/10% 1 10 8 90s
+php-apache Deployment/php-apache 97%/10% 1 10 10 105s
+php-apache Deployment/php-apache 44%/10% 1 10 10 2m
+php-apache Deployment/php-apache 39%/10% 1 10 10 2m15s
+php-apache Deployment/php-apache 18%/10% 1 10 10 2m30s
+php-apache Deployment/php-apache 2%/10% 1 10 10 2m45s
+php-apache Deployment/php-apache 0%/10% 1 10 10 3m
+php-apache Deployment/php-apache 0%/10% 1 10 10 7m31s
+php-apache Deployment/php-apache 0%/10% 1 10 2 7m46s
+php-apache Deployment/php-apache 0%/10% 1 10 1 8m1s
+```
+
+테스트가 완료 되면 리소스를 삭제합니다.
+
+```bash
+root@jakelee:~# kubectl delete deployment.apps/php-apache service/php-apache horizontalpodautoscaler.autoscaling/php-apache
+deployment.apps "php-apache" deleted
+service "php-apache" deleted
+horizontalpodautoscaler.autoscaling "php-apache" deleted
+```
+
+
+
+
+
+### 참고 자료
+
+
+
+목록 - blog
+
+
+- 커피고래 : https://coffeewhale.com/categories/#kubernetes
+
+- 60살까지 개발자로 살기 : https://jerryljh.tistory.com/category/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4%20%EA%B5%90%EC%9C%A1
+
+- 민현기 : https://medium.com/dtevangelist
+- subicura : https://subicura.com/k8s/
+- wonizz : https://blog.wonizz.tk/
+- wooody92 블로그 : https://wooody92.github.io/categories/#
+- 아리수 : https://arisu1000.tistory.com/
+- chhanz : https://chhanz.github.io/
+- devops story : https://cwal.tistory.com/21
+- microk8s : https://sarc.io/index.php/cloud/2197-microk8s
+- alice : https://m.blog.naver.com/PostList.naver?blogId=alice_k106
+- d4v1d : https://velog.io/@rhee519
+
+
+목록 - youtube
+
+
+- 악분일상
+
+- 인프런 초급 : https://youtu.be/qLlo7MAJvT0
+
\ No newline at end of file