2024년 3월 초, docker가 익숙해서 끝까지 버티고 있던 kubernetes v1.23.x 개발 환경 설치에 돌연 문제가 생겼다. Ubuntu에서 kubeadm, kubelet, kubectl 등을 apt로 설치할 수 없게 된 것이다. 원인과 해결 방법에 대해 알아보자.
Kubernetes apt install 에러 현상과 원인
K8s 설치 시 어떤 현상이 발생하는가?
2024년 3월 초부터 ubuntu에 apt install 명령어를 통해 kubeadm, kubelet, kubectl이 설치되지 않는다. 기존 사용하던 개발 환경은 다음과 같다.
위 명령어를 통해 kubernetes 설치를 진행하면, 다음과 같은 에러 메시지를 확인할 수 있다.
master: deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main
master: Hit:1 https://download.docker.com/linux/ubuntu jammy InRelease
master: Hit:3 http://ports.ubuntu.com/ubuntu-ports jammy InRelease
master: Ign:2 https://packages.cloud.google.com/apt kubernetes-xenial InRelease
master: Hit:4 http://ports.ubuntu.com/ubuntu-ports jammy-updates InRelease
master: Err:5 https://packages.cloud.google.com/apt kubernetes-xenial Release
master: 404 Not Found [IP: 142.250.76.142 443]
master: Hit:6 http://ports.ubuntu.com/ubuntu-ports jammy-backports InRelease
master: Get:7 http://ports.ubuntu.com/ubuntu-ports jammy-security InRelease [110 kB]
master: Reading package lists...
master: E: The repository 'https://apt.kubernetes.io kubernetes-xenial Release' does not have a Release file.
master:
master: WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
master:
master: Reading package lists...
master: Building dependency tree...
master: Reading state information...
master: E: Unable to locate package kubelet
master: E: Unable to locate package kubectl
master: E: Unable to locate package kubeadm
원인은 package repository에 있다!
위의 에러 메시지를 살펴보면, kubernetes 관련 package 목록을 가져오기 위해 package repository에 접속을 하는데, 404 에러가 발생하고 있음을 알 수 있다. 최근까지 잘 되던 것이 갑자기 되지 않으니까 당황스럽다. 원인은 외부에 있음을 직감하고, kubernetes 공식 문서들을 샅샅이 뒤져보았다.
아니나 다를까, package repository가 대체된다는 안내사항이 있었다. 해당 글에서는 다음과 같이 안내하고 있다.
On behalf of Kubernetes SIG Release, I am very excited to introduce the Kubernetes community-owned software repositories for Debian and RPM packages: pkgs.k8s.io! The new package repositories are replacement for the Google-hosted package repositories (apt.kubernetes.io and yum.kubernetes.io) that we’ve been using since Kubernetes v1.5.
ℹ️ Update (January 12, 2024): the legacy Google-hosted repositories are going away in January 2024. Check out the deprecation announcement for more details about this change.
기존 사용하던 package repository가 2024년 1월부터 아예 사라질 것이라고 되어 있는데, 3월까지 잘 사용한 것도 기적이었던 것이다. 평소 부지런하게 follow up하지 않았던 스스로를 반성하게 된다.
Package repository 관련 문제 해결 방법
Package repository deprecation에 대응하기
안내사항에 따르면, 다음과 같이 package repository를 변경한 후 apt install을 진행하면 된다.
master: Reading package lists...
master: E: Failed to fetch https://pkgs.k8s.io/core:/stable:/v1.23/deb/InRelease 403 Forbidden [IP: 54.192.175.103 443]
master: E: The repository 'https://pkgs.k8s.io/core:/stable:/v1.23/deb InRelease' is not signed.
한 걸음 나아가긴 했다. 404 Not Found 에러 대신 403 Forbidden 에러가 발생하는 것으로 보아, 적어도 새로운 package repository는 서비스를 하고 있긴 하다는 것을 확인할 수 있다. 그렇다면, 왜 이런 현상이 발생하는 것일까? 이 문제에 대한 원인도 앞서 언급한 안내 글에서 찾을 수 있었다.
The new Kubernetes package repositories contain packages beginning with those Kubernetes versions that were still under support when the community took over the package builds. This means that the new package repositories have Linux packages for all Kubernetes releases starting with v1.24.0.
새로운 package repository는 v1.24.0 이상의 kubernetes만 제공한다는 것이다. 결국, kubernetes의 버전을 올려야 한다.
Docker를 버린 kubernetes v1.24로의 여정
Kubernetes v1.23.x와 v1.24.x 사이에는 큰 차이점이 존재한다. Kubernetes v1.24.0부터는 docker를 버렸다는 것이다. 공식 문서의 내용을 간단히 요약하면 다음과 같다.
1.24 버전 이전에는 docker라는 specific한 CRI를 사용하고 있었다.
Kubernetes에서 docker 외에도 다양한 CRI를 지원하기 위해, CRI standard라는 것을 만들었다.
Docker는 CRI standard를 따르지 않고 있다.
Kubernetes는 docker 지원을 위해 dockershim이라는 코드를 만들어서 제공했다.
Kubernetes 개발 참여자들이 docker라는 특수 CRI를 위해 별도로 시간을 할애하는 것이 부담스럽다.
Kubernetes v1.24부터 dockershim 지원 안하기로 했다.
실제로, kubernetes v1.24 버전을 설치할 때 docker를 사용하려고 하면, 다음과 같은 에러를 만날 수 있다.
master: [WARNING SystemVerification]: missing optional cgroups: blkio
master: error execution phase preflight: [preflight] Some fatal errors occurred:
master: [ERROR CRI]: container runtime is not running: output: time="2024-03-05T00:42:52-08:00" level=fatal msg="validate service connection: CRI v1 runtime API is not implemented for endpoint \"unix:///var/run/containerd/containerd.sock\": rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService"
master: , error: exit status 1
master: [preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...`
master: To see the stack trace of this error execute with --v=5 or higher
master: failed to load admin kubeconfig: open /root/.kube/config: no such file or directory
master: To see the stack trace of this error execute with --v=5 or higher
눈물과 함께 kubernetes v1.23과 docker와 작별할 시간이다. 앞서 살펴본 package repository 설정 부분에서 버전을 지정해주는 문자열을 변경하자.
이후 apt-cache policy kubeadm 명령어를 통해 설치 가능한 버전을 조회해보고 apt install을 통해 설치를 진행하면 된다. 이때 주의할 점은, docker가 아니라 containerd를 사용할 것이기 때문에 설치하려는 kubernetes 버전과 호환되는 containerd 버전을 알아보고 미리 설치해두어야 한다는 것이다. Containerd와 kubernetes 버전 호환 관계는 이 문서를 확인하면 된다.
버전 관리 서버를 private 하게 내부에서 관리할 필요가 있다는 판단하여 docker를 가지고 GitLab 서버를 구축하였다. 구축 과정을 블로그에 남긴다.
DB서버를 사용하는 내부 서버가 있다. 그곳에다 GitLab서버를 구축하려고하였다. 그래서 다른것 하나 생각치않고 docker부터 떠올렸다. 이유는 해당 서버에 DB서버가 구축되어 있기 때문에 GitLab서버를 그대로 설치해버리면 기존에 사용하던 시스템이 망가질 우려가 있었기 때문이다.
따라서 기존의 시스템에 전혀 영향이 가지 않게끔 도커를 이용하여 GitLab 서버를 설치하였다.
GitLab Docker images The GitLab Docker images are monolithic images of GitLab running all the necessary services in a single container. If you instead want to install GitLab on Kubernetes, see GitLab Helm Charts. Find GitLab’s official Docker image at: T
docs.gitlab.com
나 또한 gitlab 서버 설치할 때 공식 가이드를 참고하여 설치하였다. 내가 작성한 docker-compose.yml 파일의 내용은 아래와 같다.
ports : 8829 -> 80, 4443 -> 443, 2224 -> 22 로 매핑시켰다. 이유는 혹시 모를 중복되어지는 포트가 있을 수 있기 때문이다. 그리고 해당 서버에서 벌써 22번 포트를 사용하고 있었기에 ssh 포트를 변경하면서 나머지도 변경하였다.
external_url : ‘http://호스트명:포트번호’ 이런식으로 입력한다. 이 주소는 추후 gitlab에서의 clone을 이용할 때 사용되어지는 주소이다. 포트번호를 적지 않으면 gitlab에서 포트번호가 보이지 않게 되어 clone할 때 매번 포트 번호를 적어줘야하는 귀찮은 일이 발생하므로 필히 적어주는 것이 좋을 것이다.
gitlab_rails[‘gitlab_shell_ssh_port’] : 이 값에 새로운 ssh 포트 값을 입력하였다.
nginx[‘listen_port’] : external_url 옵션에 포트번호가 들어가는데 이 옵션이 빠진다면, gitlab 접속이 불가능할 것이다. 그러므로 nginx의 포트를 지정해주었다.
nginx[‘client_max_body_size’] : 만약 큰 용량의 파일을 올릴 수 있다면 용량을 크게 잡는다. 한번에 올릴 수 있는 용량의 크기를 정하는 옵션이다.
volumes : gitlab 서버 컨테이너를 삭제하여도 기존의 쌓였던 데이터들을 고스란히 보관하기 위하여 volumes 옵션을 주었다.
volumes – backups : 이것은 추후 gitlab 서버 데이터의 백업 및 복구를 편하게 하기 위하여 설정한 것이다.
위의 파일을 작성했다면 다음의 명령으로 gitlab 서버 컨테이너를 실행시킨다.
$ docker-compose up -d
참고로 gitlab 서버가 정지되었을 경우 아래의 명령처럼 gitlab 서버 컨테이너를 실행시키면 된다.
$ docker start gitlab
GitLab 이미지 Update
gitlab의 버전을 업데이트할 때는 gitlab 컨테이너를 제거하고, gitlab 도커 이미지를 업데이트 한 후 그 이미지로 컨테이너를 만들면 된다.
// 도커 컨테이너 정지
$ docker container stop gitlab
// 혹은 아래와 같이 docker-compose로 컨테이너를 볼륨과 함께 정지 후 제거
$ docker-compose -f <docker-compose.yml 경로> down -v
// 도커 이미지 제거
$ docker image rm gitlab
// gitlab 최신버전 이미지 가져오기
$ docker image pull gitlab/gitlab-ce:latest
// gitlab 실행
$ docker-compose up -d
하지만 docker-compose.yml 파일을 작성했다면 아래와 같이 간단한 방법으로 update를 할 수 있다.
// docker-compose.yml 파일 위치로 이동 후// gitlab update
$ docker-compose pull
// gitlab 재실행
$ docker-compose up -d
// 혹은, 컨테이너를 완전하게 삭제 후 실행
$ docker-compose up -d --force-recreate
이메일 발송 설정
이메일을 설정해 놓으면 사용자가 비밀번호를 분실했을 경우, 비밀번호 재설정을 이메일로 받아 볼 수 있다. 이런 설정을 하려면 gitlab 컨테이너에 접속하여 설정 파일을 수정해야 한다.
// gitlab 컨테이너 접속
$ docker container exec -it gitlab /bin/bash
// 설정 파일 수정
$ vim /etc/gitlab/gitlab.rb
아래의 공식 가이드 문서에 가면 본인이 원하는 이메일 설정 방법이 자세히 나와 있으므로 그것을 보고 따라하면 되겠다.
SMTP settings If you would rather send application email via an SMTP server instead of via Sendmail, add the following configuration information to /etc/gitlab/gitlab.rb and run gitlab-ctl reconfigure. cautionYour smtp_password should not contain any Strin
docs.gitlab.com
이메일 설정을 모두 마쳤다면 다음과 같은 명령을 통해 변경된 설정을 적용하자.
// /etc/gitlab/gitlab.rb 파일을 변경한 후에는 항상 다음의 명령을 통해 변경 사항을 적용시켜줘야 한다.
gitlab-ctl reconfigure
GitLab 서버 Backup
/var/opt/gitlab/backups 디렉토리를 공유 디렉토리로 매핑시켰다. 그것은 gitlab 서버 복원을 편하게 하기 위함이다.
백업된 gitlab 데이터 파일을 /var/opt/gitlab/backups 디렉토리 즉, 현재 셋팅해서는 ~/Documents/gitlab/backups 디렉토리에 넣고 아래의 명령을 실행하면 gitlab 데이터가 복원된다.
Back up and restore GitLab GitLab provides Rake tasks for backing up and restoring GitLab instances. An application data backup creates an archive file that contains the database, all repositories and all attachments. You can only restore a backup to exact
docs.gitlab.com
참고로, 실행중인 gitlab 서버 컨테이너 백업/복구의 명령은 다음과 같다.
// gitlab 서버 데이터 백업 명령어
docker container exec -t <컨테이너 이름> gitlab-backup create
// gitlab 서버 데이터 복구 명령어
docker exec -it <컨테이너 이름> gitlab-backup restore
이제 이미지도 준비되었고, yaml 파일만 생성하여 기동해보면 된다.
도커와는 다르게 쿠버네티스의 경우에는 쿠버네티스 클러스터 내에서 동작하기 때문에 생성된 컨테이너의 IP 또한 클러스터 내의 CNI 네트워크 대역으로 할당된다.
그렇기 때문에 외부에서 해당 컨테이너에 접근하여 서비스를 호출하려면 Service를 통해 가능하다. AWS나 GCP같은 Public Cloud의 경우에는 Service에서 LoadBalnancer를 제공하여 외부로 노출할 IP를 할당 받지만, 우리가 테스트하는 방식과 같이 Baremetal(On-Premise) 환경에서는 LoadBalancer 기능이 없다. 그래서 LoadBalancer타입을 지원하게끔 하기 위해 MetalLB라는 놈을 사용해주어야 한다.
=================================
metallb.yaml 파일을 이용하여 파드와 기타 등등의 서비스를 실행한다.
이후, metallb-system 네임스페이스에 있는 리소스들을 조회해본다.
=================================
root@master:~/yaml# kc get all -n metallb-system
NAME READY STATUS RESTARTS AGE
pod/controller-675d6c9976-d5k57 1/1 Running 0 9s
pod/speaker-h4l8n 1/1 Running 0 9s
pod/speaker-rm54l 1/1 Running 0 9s
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/speaker 2 2 2 2 2 beta.kubernetes.io/os=linux 9s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/controller 1/1 1 1 9s
NAME DESIRED CURRENT READY AGE
replicaset.apps/controller-675d6c9976 1 1 1 9s
이제 3-Tier환경의 컨테이너를 생성할 차례다.
yaml 파일의 경우 아래와 같다.
모든 항목들을 세세히 설명할 수는 없고 하나씩 찾아보면 기본적인 설정(포트/환경변수)만 되어있음을 볼 수 있다.
이 글에서는 가볍게 구성해보는 것이기 때문에 다음 글에서 좀 더 yaml 문법이나 파라미터 값을 공부해보아야 겠다.
(중략)
…
update-alternatives: using /usr/lib/jvm/java-11-openjdk-amd64/bin/unpack200 to provide /usr/bin/unpack200 (unpack200) in auto mode
update-alternatives: using /usr/lib/jvm/java-11-openjdk-amd64/lib/jexec to provide /usr/bin/jexec (jexec) in auto mode
Setting up libxdmcp-dev:amd64 (1:1.1.3-0ubuntu1) …
Setting up x11proto-core-dev (2019.2-1ubuntu1) …
Setting up libxcb1-dev:amd64 (1.14-2) …
Setting up libx11-dev:amd64 (2:1.6.9-2ubuntu1.2) …
Setting up libxt-dev:amd64 (1:1.1.5-1) …
1-3. JAVA_HOME 설정
> 현재 java -version으로 버전 확인 시 설치한 자바로 설정되어 있지 않기 때문에 아래와 같이 변경이 필요하다.
root@master:/home/src# java -version
openjdk version “1.8.0_292”
OpenJDK Runtime Environment (build 1.8.0_292-8u292-b10-0ubuntu1~20.04-b10)
OpenJDK 64-Bit Server VM (build 25.292-b10, mixed mode)
1-4. 소스 Build
> 이제 소스와 컴파일 할 JDK와 maven도 준비되었기 때문에 Compile을 해보도록 하자
소스 경로에 들어가 압축을 풀고 아래와 같이 빌드를 진행해준다.
빌드 전에 잠시 소스 수정이 필요하다. pom.xml에 mysql 연동 부분에 localhost:3306을 mysql-petclinic:3306으로 수정하자. was와 db가 다른 컨테이너로 기동되기 때문에 localhost를 인식할 수 없다. 그러나 mysql-petclinic은 컨테이너 명으로 link가 걸려 통신이 가능하다..
root@master:/home/src/spring-framework-for-petclinic/spring-framework-for-petclinic# mvn clean package -Dmaven.test.skip=true -P MySQL
…(중략)
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 16.802 s
[INFO] Finished at: 2021-11-07T13:54:37+09:00
[INFO] ————————————————————————
========================================================================= 빌드를 하고 나면, 소스 경로 하위의 target 디렉토리에 petclinic.war 소스 파일이 생성된다.
root@master:/home/src/spring-framework-for-petclinic/spring-framework-for-petclinic/target# ls -arlt|grep petclinic.war
-rw-r–r– 1 root root 41605349 11월 7 13:54 petclinic.war
1-5. WAS 이미지 생성
> 이제 만든 소스를 활용하여 tomcat 이미지를 만들어보도록 하자.
우선 이미지 생성을 위해 Dockerfile을 생성하고 이전에 만들었던 petclinic.war 파일을 같은 디렉토리에 위치시킨다.
root@master:/home/dockerfile/petclinic# pwd
/home/dockerfile/petclinic
root@master:/home/dockerfile/petclinic# ls -arlt
total 40644
drwxr-xr-x 5 root root 4096 11월 7 14:06 ..
-rw-r–r– 1 root root 41605349 11월 7 14:06 petclinic.war
-rw-r–r– 1 root root 64 11월 7 14:06 Dockerfile
drwxr-xr-x 2 root root 4096 11월 7 14:06 .
하기는 Dockerfile 내용이다.
root@master:/home/dockerfile/petclinic# cat Dockerfile
From tomcat:8-jre8
ADD petclinic.war /usr/local/tomcat/webapps/
> tomcat 컨테이너에서 mysql 컨테이너의 ip로 접속할 수 있겠지만, static ip가 아니기 때문에 mysql 컨테이너의 이름으로 접속을 해야 할 필요가 있다. 이럴 경우 link 옵션을 통해 컨테이너를 구동하면 tomcat 컨테이너에서 mysql 컨테이너로 이름으로 접속할 수 있다. link 옵션으로 구동하면 톰캣 컨테이너 안의 /etc/hosts에 링크된 mysql 컨테이너 hostname과 ip가 들어간다.
$ kubectl apply -f deploy/kubernetes/
clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created
clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator created
rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader created
apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io created
serviceaccount/metrics-server created
deployment.apps/metrics-server created
service/metrics-server created
clusterrole.rbac.authorization.k8s.io/system:metrics-server created
clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server created
중간에 metrics.k8s.io라는 API가 생성되어 Api server에 등록된 것을 확인할 수 있습니다.
$ kubectl apply -f autoscaler.yaml
horizontalpodautoscaler.autoscaling/php-apache created
hpa커맨드를 통해 현재 hpa에 감지되는 시스템 부하정도와 관리하는 pod의 개수를 확인할 수 있습니다.
$ kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache 0%/50% 1 10 1 18s
아직은 서버로 어떠한 요청도 하지 않았기 때문에, 현재 cpu소비량은 0%임을 알 수 있습니다. (TARGET은 deployment에 의해 제어되는 pod들의 평균을 뜻합니다.)
부하테스트
부하가 증가함에 따라 오토스케일러가 어떻게 반응하는지 살펴보겠습니다.
창을 하나 더 띄워서 php-apache 서비스에 무한루프 쿼리를 전송합니다.
$ kubectl run -it--rm load-generator --image=busybox /bin/sh
If you don't see a command prompt, try pressing enter.
/ #
/ # while true; do wget -q -O- http://php-apache.default.svc.cluster.local; done
OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!...
1~2분 지난 뒤에 hpa커맨드로 부하상태를 살펴보면 TARGET의 수치가 높아진 것을 확인할 수 있습니다.
$ kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache 248%/50% 1 10 1 9m7s
그리고 deployment 컨트롤러를 확인해보면 pod의 replica수가 5개까지 늘어난 것을 확인할 수 있습니다.
$ kubectl get deploy php-apache
NAME READY UP-TO-DATE AVAILABLE AGE
php-apache 5/5 5 5 12m
busybox컨테이너를 띄운 터미널에서 Ctrl+C로 부하 발생을 중단시키고, 몇 분 후에 결과를 확인합니다.
$ kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache 0%/50% 1 10 5 11m
cpu의 사용량이 0%까지 떨어졌고, deployment의 pod replica수도 1개로 줄어든 것을 확인할 수 있습니다.
$ kubectl get deploy php-apache
NAME READY UP-TO-DATE AVAILABLE AGE
php-apache 1/1 1 1 19m
kubernetes에서 HA를 구성하는 방법으로 stacked etcd, external etcd 두가지 방법이 있다. 그림 1의 stacked etcd는 control plane node에서 etcd가 작동하는 반면, 그림 2의 external etcd는 control plane node와 etcd가 다른 노드에서 작동한다. HA를 구성하기 위해서는 쿼럼이 과반수를 초과해야만 하기 때문에 최소 3대 이상(3,5,7,…)의 노드를 필요로 한다. 이번 시간에는 stacked etcd 를 구성하는 방법에 대해 알아보고자 한다.
[그림 1] HA 토폴로지 – stacked etcd[그림 1] HA 토폴로지 – external etcd
구성환경
Ubuntu 18.04.1, Docker 19.03.8, Kubernet v1.17.4
사전 준비
Docker, Kubernet이 미리 설치 되어 있어야한다.
로드 발란서(Load Balancer)
– dns 로드 발란서 사용, apisvr 라운드로빈(round-robin) 활용, 참고로 도메인은 hoya.com
apisvr IN A 192.168.0.160
IN A 192.168.0.161
IN A 192.168.0.162
–upload-certs : control plane 인스턴스에서 공유해야 하는 인증서를 업로드(자동배포), 수동으로 인증서를 복사할 경우는 이 옵션 생략
control plane 초기화(kubeadm init)를 진행하면 아래와 같은 내용이 출력될 것이다. 아래 내용을 복사해 놓도록 한다. 이 명령어를 이용하여 클러스터에 조인(join) 할수 있다. 파란색글씨(위쪽)는 control plane 노드에서 실행, 주황색 글씨(아래)는 worker node에서 실행
You can now join any number of control-plane node by running the following command on each as a root:
kubeadm join apisvr.hoya.com:6443 –token 9vr73a.a8uxyaju799qwdjv –discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 –control-plane –certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use kubeadm init phase upload-certs to reload certs afterward.
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join apisvr.hoya.com:6443 –token 9vr73a.a8uxyaju799qwdjv –discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866
참고: 인증서를 다시 업로드하고 새 암호 해독 키를 생성하려면 이미 클러스터에 연결된 control plane 노드에서 다음 명령을 사용하십시오.
shell> sudo kubeadm init phase upload-certs –upload-certs
W0322 16:20:40.631234 101997 validation.go:28] Cannot validate kube-proxy config – no validator is available
W0322 16:20:40.631413 101997 validation.go:28] Cannot validate kubelet config – no validator is available
[upload-certs] Storing the certificates in Secret “kubeadm-certs” in the “kube-system” Namespace
[upload-certs] Using certificate key:
7f97aaa65bfec1f039c4dbdf3a2073de853c708bd4d9ff9d72b776b0f9874c9d
참고 : 클러스터를 control plane 조인(join)하는 데 필요한 전체 ‘kubeadm join’ 플래그를 출력
shell> kubectl get nodes
NAME STATUS ROLES AGE VERSION
master1 Ready master 41s v1.17.4
master2 Ready master 3m59s v1.17.4
master3 Ready master 92s v1.17.4
shell>
** kubectl delete node로 control plane노드 를 제거하지 않도록 한다. 만약 kubectl delete node로 삭제하는 경우 이후 추가 되는 control plane 노드들은 HA에 추가 될수 없다. 그 이유는 kubeadm reset은 HA내 다른 control plane 노드의 etcd에서 etcd endpoint 정보를 지우지만 kubectl delete node는 HA 내의 다른 controle plane 노드에서 etcd endpoint 정보를 지우지 못하기 때문이다. 이로 인해 이후 HA에 추가되는 control plane 노드는 삭제된 노드의 etcd endpoint 접속이 되지 않기 때문에 etcd 유효성 검사 과정에서 오류가 발생하게 된다.
– control plane 노드를 kubectl delete node 로 삭제후 control plane 노드 추가시 오류 메시지
shell> kubeadm join ….. –control-plane –certyficate-key
… 생략 …
[check-etcd] Checking that the etcd cluster is healthy
error execution phase check-etcd: etcd cluster is not healthy: failed to dial endpoint https://192.168.0.159:2379 with maintenance client: context deadline exceeded
To see the stack trace of this error execute with –v=5 or higher
$ ls
config.yml dashboard docker-compose.yml registry-data stack
docker-compose up을 실행하면 위처럼 registry-data와 stack 디렉토리가 생기는걸 확인할 수 있다. registry-data 디렉토리를 registry 서비스의 /var/lib/registry 디렉토리와 마운트 시켜놓으면, registry 서비스가 중지되어도 데이터가 그대로 유지된다.
– localhost:8080 접속
registry-web 접속
registry-web에 접속하면, 현재 registry 상태를 web UI를 통해 확인할 수 있다.
private registry에 이미지 업로드
docker swarm service에 사용할 이미지를 다운로드 받고, 직접 private registry에 이미지를 업로드 해본다.
– docker pull
$ docker image pull subicura/whoami:1
위의 이미지를 받는다. 브라우저로 접속하면 hostname을 출력해주는 docker 이미지이다.
– docker tag
$ docker image tag subicura/whoami:1 localhost:5000/example/whoami:latest
private registry의 포트가 5000이므로, 위와같이 docker image tag 명령어를 통해 이미지 이름과 tag 정보를 변경한다. 이미지의 첫 항목이 image pull 진행 시, 이미지가 올라가는 도메인 정보이다.
이미지가 올라간것을 확인할 수 있다. 이제 docker swarm 노드에서 이미지를 다운받아 사용할 수 있다.
docker swarm 구성하기
– docker swarm init
$ docker exec -it manager docker swarm init
Swarm initialized: current node (5ywj85cw3tdz8ioe71v1xlyzn) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-1gxj86agzmecefcd6mwjproa96hw7e3gu0plsdhuw9110fuc1r-7q4434lsegri8la2n9qgggsrd 172.22.0.4:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
manager 컨테이너에서 docker swarm init 명령어를 입력한다. 위처럼 docker swarm join 부분 전체를 복사한다. worker 노드들에서 전부 입력하면 된다.
– docker swarm join
$ docker exec -it worker01 docker swarm join --token SWMTKN-1-1gxj86agzmecefcd6mwjproa96hw7e3gu0plsdhuw9110fuc1r-7q4434lsegri8la2n9qgggsrd 172.22.0.4:2377
This node joined a swarm as a worker.
$ docker exec -it worker02 docker swarm join --token SWMTKN-1-1gxj86agzmecefcd6mwjproa96hw7e3gu0plsdhuw9110fuc1r-7q4434lsegri8la2n9qgggsrd 172.22.0.4:2377
This node joined a swarm as a worker.
$ docker exec -it worker03 docker swarm join --token SWMTKN-1-1gxj86agzmecefcd6mwjproa96hw7e3gu0plsdhuw9110fuc1r-7q4434lsegri8la2n9qgggsrd 172.22.0.4:2377
This node joined a swarm as a worker.
Kubernetes는 온프레미스 서버 또는 하이브리드 클라우드 환경에서 대규모로 컨테이너화된 애플리케이션을 오케스트레이션 및 관리하기 위한 도구입니다. Kubeadm은 사용자가 모범 사례 시행을 통해 프로덕션 준비 Kubernetes 클러스터를 설치할 수 있도록 Kubernetes와 함께 제공되는 도구입니다. 이 튜토리얼은 kubeadm을 사용하여 Ubuntu 20.04에 Kubernetes 클러스터를 설치하는 방법을 보여줍니다.
Kubernetes 클러스터 배포에는 두 가지 서버 유형이 사용됩니다.
마스터 : Kubernetes 마스터는 Kubernetes 클러스터의 포드, 복제 컨트롤러, 서비스, 노드 및 기타 구성 요소에 대한 제어 API 호출이 실행되는 곳입니다.
Node : Node는 컨테이너에 런타임 환경을 제공하는 시스템입니다. 컨테이너 포드 세트는 여러 노드에 걸쳐 있을 수 있습니다.
실행 가능한 설정을 위한 최소 요구 사항은 다음과 같습니다.
메모리: 컴퓨터당 2GiB 이상의 RAM
CPU: 컨트롤 플레인 머신에 최소 2개의 CPU 가 있습니다.
컨테이너 풀링을 위한 인터넷 연결 필요(개인 레지스트리도 사용할 수 있음)
클러스터의 머신 간 전체 네트워크 연결 – 개인 또는 공용입니다.
Ubuntu 20.04에 Kubernetes 클러스터 설치
My Lab 설정에는 3개의 서버가 있습니다. 컨테이너화된 워크로드를 실행하는 데 사용할 하나의 컨트롤 플레인 머신과 두 개의 노드. 예를 들어 HA용 제어 평면 노드 3개 를 사용하여 원하는 사용 사례 및 부하에 맞게 노드를 더 추가할 수 있습니다 .
서버 유형
서버 호스트 이름
명세서
주인
k8s-master01.computingforgeeks.com
4GB 램, 2vcpus
노동자
k8s-worker01.computingforgeeks.com
4GB 램, 2vcpus
노동자
k8s-worker02.computingforgeeks.com
4GB 램, 2vcpus
1단계: Kubernetes 서버 설치
Ubuntu 20.04에서 Kubernetes 배포에 사용할 서버를 프로비저닝합니다. 설정 프로세스는 사용 중인 가상화 또는 클라우드 환경에 따라 다릅니다.
systemd cgroup 드라이버를 사용하려면 에서 plugins.cri.systemd_cgroup = true 를 설정 /etc/containerd/config.toml하십시오. kubeadm을 사용할 때 kubelet 용 cgroup 드라이버를 수동으로 구성하십시오.
--control-plane-endpoint : 모든 제어 평면 노드에 대한 공유 끝점을 설정합니다. DNS/IP일 수 있음
--pod-network-cidr : 포드 네트워크 추가 기능을 설정하는 데 사용됨 CIDR
--cri-socket : 런타임 소켓 경로를 설정하기 위해 컨테이너 런타임이 둘 이상인 경우 사용
--apiserver-advertise-address : 이 특정 제어 평면 노드의 API 서버에 대한 광고 주소 설정
....
[init] Using Kubernetes version: v1.22.2
[preflight] Running pre-flight checks
[WARNING Firewalld]: firewalld is active, please ensure ports [6443 10250] are open or your cluster may not function correctly
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Using existing ca certificate authority
[certs] Using existing apiserver certificate and key on disk
[certs] Using existing apiserver-kubelet-client certificate and key on disk
[certs] Using existing front-proxy-ca certificate authority
[certs] Using existing front-proxy-client certificate and key on disk
[certs] Using existing etcd/ca certificate authority
[certs] Using existing etcd/server certificate and key on disk
[certs] Using existing etcd/peer certificate and key on disk
[certs] Using existing etcd/healthcheck-client certificate and key on disk
[certs] Using existing apiserver-etcd-client certificate and key on disk
[certs] Using the existing "sa" key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Using existing kubeconfig file: "/etc/kubernetes/admin.conf"
[kubeconfig] Using existing kubeconfig file: "/etc/kubernetes/kubelet.conf"
[kubeconfig] Using existing kubeconfig file: "/etc/kubernetes/controller-manager.conf"
[kubeconfig] Using existing kubeconfig file: "/etc/kubernetes/scheduler.conf"
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
W0611 22:34:23.276374 4726 manifests.go:225] the default kube-apiserver authorization-mode is "Node,RBAC"; using "Node,RBAC"
[control-plane] Creating static Pod manifest for "kube-scheduler"
W0611 22:34:23.278380 4726 manifests.go:225] the default kube-apiserver authorization-mode is "Node,RBAC"; using "Node,RBAC"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[apiclient] All control plane components are healthy after 8.008181 seconds
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.21" in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node k8s-master01.computingforgeeks.com as control-plane by adding the label "node-role.kubernetes.io/master=''"
[mark-control-plane] Marking the node k8s-master01.computingforgeeks.com as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[bootstrap-token] Using token: zoy8cq.6v349sx9ass8dzyj
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:
kubeadm join k8s-cluster.computingforgeeks.com:6443 --token sr4l2l.2kvot0pfalh5o4ik \
--discovery-token-ca-cert-hash sha256:c692fb047e15883b575bd6710779dc2c5af8073f7cab460abd181fd3ddb29a18 \
--control-plane
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join k8s-cluster.computingforgeeks.com:6443 --token sr4l2l.2kvot0pfalh5o4ik \
--discovery-token-ca-cert-hash sha256:c692fb047e15883b575bd6710779dc2c5af8073f7cab460abd181fd3ddb29a18
$ kubectl cluster-info
Kubernetes master is running at https://k8s-cluster.computingforgeeks.com:6443
KubeDNS is running at https://k8s-cluster.computingforgeeks.com:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/bgppeers.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/blockaffinities.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/clusterinformations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/felixconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworksets.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/hostendpoints.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamblocks.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamconfigs.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamhandles.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/kubecontrollersconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networksets.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/apiservers.operator.tigera.io created
customresourcedefinition.apiextensions.k8s.io/imagesets.operator.tigera.io created
customresourcedefinition.apiextensions.k8s.io/installations.operator.tigera.io created
customresourcedefinition.apiextensions.k8s.io/tigerastatuses.operator.tigera.io created
namespace/tigera-operator created
Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
podsecuritypolicy.policy/tigera-operator created
serviceaccount/tigera-operator created
clusterrole.rbac.authorization.k8s.io/tigera-operator created
clusterrolebinding.rbac.authorization.k8s.io/tigera-operator created
deployment.apps/tigera-operator created
.....
installation.operator.tigera.io/default created
apiserver.operator.tigera.io/default created
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.21" ConfigMap in the kube-system namespace
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
제어 플레인에서 아래 명령을 실행하여 노드가 클러스터에 합류했는지 확인합니다.
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master01.computingforgeeks.com Ready master 10m v1.22.2
k8s-worker01.computingforgeeks.com Ready <none> 50s v1.22.2
k8s-worker02.computingforgeeks.com Ready <none> 12s v1.22.2
$ kubectl get nodes -o wide
Prometheus는 Kubernetes 클러스터의 고급 메트릭 기능에 액세스할 수 있는 완전한 솔루션입니다. Grafana는 Prometheus 데이터베이스에 수집 및 저장되는 메트릭의 분석 및 대화형 시각화에 사용됩니다. Kubernetes 클러스터에서 전체 모니터링 스택을 설정하는 방법에 대한 완전한 가이드가 있습니다.
이 글에서는 기본적인 Docker Registry 설치법에 대해 다룰 것이다. 정말 자세한 내용은 공식 홈페이지를 참조하면 된다. Docker Registry란 Docker Image를 관리하는 Docker Hub 같은 Respository를 말한다. 개별적으로 Docker Image를 관리 할 일이 생기면 필수라고 생각된다. Docker Registry를 설치하기 위해서, docker와 docker-compose가 필요하다. 설치는 아래 글을 참고하자.
간단한 설치
공식홈페이지에 보면 아래처럼 단 한 줄로 Registry를 설치 할 수 있다고 나와있다.
$ docker run -d -p 5000:5000 registry:2.6$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
860c11dc6921 registry:2.6 "/entrypoint.sh /e..." 48 seconds ago Up 47 seconds 0.0.0.0:5000->5000/tcp brave_ptolemy
기본 설정대로 사용하면 제약사항이 너무 많다. 꼭 필요하다고 생각되는 부분만 변경해보도록 하겠다.
log
로그는 공식 홈페이지를 보고 입맛에 맞게 설정하면 된다. 지금 단계에서 크게 중요하지 않다.
storage
기본 설정대로면 container안에 디스크에 Docker Image가 저장된다. 이렇게되면 docker registry가 삭제되면 데이터가 같이 날아가게되므로, 설정은 그대로 두고 host에 있는 디렉토리를 mount 해줄 필요가 있다. 혹은 cloud storage를 사용하면 좋은데 대표적인 예로 AWS S3가 있다. 다른 옵션을 더 보고싶으면 공식 홈페이지 문서를 참고하자.
기본 설정대로면 캐쉬를 메모리에 하도록 되어있다. 이는 쓸데 없이 메모리를 사용하게 된다고 생각이 되는데, 다른 옵션으로 redis를 사용 할 수 있다. 캐쉬를 redis로 사용하기 위해서는 redis를 실행하고 설정을 따로 해줘야하는데, redis image를 사용하도록하자.
storage 부분이 redis 설정까지 추가해 이렇게 변경된다. redis addr에 redis:6379를 쓴 것은 redis를 docker container로 실행하고 link로 연결해 줄 것이기 때문이다.
기본 설정대로면 Docker Registry에 접근하기 위해서 그 어떤 인증도 필요하지 않다. Docker Registry V2부터 3rd party 인증시스템을 도입 할 수 있도록 JWT Token Base 인증 서버를 별도로 구현 할 수 있다. 이 부분은 여기서 함께 다루기엔 너무 복잡하므로 상대적으로 간단한 Basic Authorization을 이용하여 인증 시스템을 설정해보도록 하자.
설정을 살펴보면 realm, path를 지정하도록 되어있다. realm은 원하는 값을 넣어주고 path에는 .htpasswd 파일 경로를 넣어준다. docker registry에서 사용 할 .htpasswd 파일은 아래 명령어를 이용해 만들 수 있다. 아이디가 admin, 비밀번호가 1234인 경우이다.
자세히 하려면 봐야 할 설정이 더 있겠지만, 정말 기본적인 설정은 끝났다. 이제 docker-compose.yml을 작성하고 디플로이를 하자.
version:3services:registry:image:registry:2.6volumes:
- /var/lib/registry:/var/lib/registry# host filesystem을 mount
- ./config.yml:/etc/docker/registry/config.yml:ro # 설정 파일 변경
- ./.htpasswd:/etc/docker/registry/.htpasswd:ro # htpasswd mountlinks:
- redis:redis # cache에 사용 할 redis container 연결ports:
- 5000:5000# 5000번을 이용해 통신depends_on:
- redis # redis가 실행된 후, registry가 실행된다.redis:image:redis:3.0.7