카테고리 보관물: docker

The end of Kubernetes version 1.23 – Ubuntu kubernetes apt install error

2024년 3월 초, docker가 익숙해서 끝까지 버티고 있던 kubernetes v1.23.x 개발 환경 설치에 돌연 문제가 생겼다. Ubuntu에서 kubeadm, kubelet, kubectl 등을 apt로 설치할 수 없게 된 것이다. 원인과 해결 방법에 대해 알아보자.

Kubernetes apt install 에러 현상과 원인

K8s 설치 시 어떤 현상이 발생하는가?

2024년 3월 초부터 ubuntu에 apt install 명령어를 통해 kubeadm, kubelet, kubectl이 설치되지 않는다. 기존 사용하던 개발 환경은 다음과 같다.

OS: jharoian3/ubuntu-22.04-arm64
Kubernetes: 1.23.6-00
Docker: 5:24.0.6-1~ubuntu.22.04~jammy

설치를 위한 명령어는 다음과 같이 사용하고 있었다.

curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-archive-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt -y install kubelet="$KUBERNETES_VERSION" kubectl="$KUBERNETES_VERSION" kubeadm="$KUBERNETES_VERSION"

위 명령어를 통해 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을 진행하면 된다.

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.23/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.23/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update

그런데, 이렇게 변경 후 테스트해도 여전히 다음과 같은 에러가 발생한다.

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 설정 부분에서 버전을 지정해주는 문자열을 변경하자.

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.24/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.24/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update

이후 apt-cache policy kubeadm 명령어를 통해 설치 가능한 버전을 조회해보고 apt install을 통해 설치를 진행하면 된다. 이때 주의할 점은, docker가 아니라 containerd를 사용할 것이기 때문에 설치하려는 kubernetes 버전과 호환되는 containerd 버전을 알아보고 미리 설치해두어야 한다는 것이다. Containerd와 kubernetes 버전 호환 관계는 이 문서를 확인하면 된다.

최종적으로 정상화한 개발 환경은 다음과 같다.

OS: jharoian3/ubuntu-22.04-arm64
Kubernetes: 1.24.17-1.1
Containerd: 1.7.0

간단하게 요약하면?

요약 정리

  • 현상: ubuntu에 apt install로 kubernetes를 설치할 때, package list를 불러오는 과정에서 에러 발생
  • 원인: 2024년 초부터 버려진 legacy package repository를 참조하고 있을 가능성 있음
  • 해결책: 새로운 package repository로 변경하면 됨

주의사항

  • 새로운 package repository는 kubernetes v1.24.0 이상만 제공
  • Docker를 쓰고 있었다면, containerd같은 다른 CRI로 변경 필요

Docker Root Directory 위치 변경

개발 중인 서버에 접근해보니 루트 디렉토리가 91% 사용 중이었습니다.

(*도커의 루트 디렉토리 증설은 불가피했음.)

초기 설치 시 /var/lib/docker/overlay 디렉토리 경로 그대로 사용

01. 루트 디렉토리 설정 확인하기

docker info | grep Root 명령어로 Docker Root Dir이 /var/lib/docker임을 확인했습니다.

[root@totoli]# docker info | grep Root
 Docker Root Dir: /var/lib/docker

02. 루트 디렉토리 설정 변경하기

도커의 설정은 /etc/docker/daemon.json를 변경하면 됩니다.
json 형식으로 작성하면 되며, data-root 요소를 작성합니다.

[root@totoli]# vi /etc/docker/daemon.json

{
  "data-root": "/docker-data/data"		# 변경하고자 할 디렉토리로 작성합니다.
}

*example mount path : "/docker-data" 
mkdir /docker-data/data
cp -rp /var/lib/docker/* /docker-data/data/

03. 설정 변경 후 도커 재기동하기

[root@totoli]# systemctl restart docker

04. 루트 디렉토리 설정 재확인하기

docker info | grep Root 명령어로 다시 한번 확인합니다.
설정했던 바와 같이 /docker-data/data로 변경됨을 확인할 수 있습니다.

[root@totoli]# docker info | grep Root
 Docker Root Dir: /docker-data/data

/var/lib/docker/~~~에서 /docker-data/data/~~~ 로 변경됨을 확인.

05. 기존 루트 디렉토리 삭제

docker info | grep Root 명령어로 다시 한번 확인합니다.
설정했던 바와 같이 /docker-data/data로 변경됨을 확인할 수 있습니다.

ls /docker-data/data
* 잘 복사된것 확인 후 .

[root@totoli]# rm /var/lib/docker -Rf

Docker를 이용한 로컬 환경에서 GitLab-CE 서버 구축 (with docker-compose)

버전 관리 서버를 private 하게 내부에서 관리할 필요가 있다는 판단하여 docker를 가지고 GitLab 서버를 구축하였다. 구축 과정을 블로그에 남긴다.

DB서버를 사용하는 내부 서버가 있다. 그곳에다 GitLab서버를 구축하려고하였다. 그래서 다른것 하나 생각치않고 docker부터 떠올렸다. 이유는 해당 서버에 DB서버가 구축되어 있기 때문에 GitLab서버를 그대로 설치해버리면 기존에 사용하던 시스템이 망가질 우려가 있었기 때문이다.

따라서 기존의 시스템에 전혀 영향이 가지 않게끔 도커를 이용하여 GitLab 서버를 설치하였다.

hub.docker.com/r/gitlab/gitlab-ce

Docker Hub

hub.docker.com


GitLab 서버 설치

gitlab 서버 설치에 사용한 이미지는 Docker Hub에서 제공하는 이미지를 사용하였다.

docker-compose를 사용한 gitlab 서버 설치의 공식 가이드는 아래의 링크에서 볼 수 있다.

docs.gitlab.com/omnibus/docker/README.html#install-gitlab-using-docker-compose

GitLab Docker images | 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 파일의 내용은 아래와 같다.

gitlab:
  image: 'gitlab/gitlab-ce:latest'
  restart: always
  hostname: '127.0.0.1'
  container_name: gitlab
  environment:
    GITLAB_OMNIBUS_CONFIG: |
      external_url 'http://127.0.0.1:8829'
      gitlab_rails['gitlab_shell_ssh_port'] = 2224
      nginx['enable'] = true
      nginx['listen_port'] = 80
      nginx['client_max_body_size'] = '10G'
    GITLAB_TIMEZONE: Asia/Seoul
  ports:
    - '8829:80'
    - '4443:443'
    - '2224:22'
  volumes:
    - '~/Documents/gitlab/config:/etc/gitlab'
    - '~/Documents/gitlab/logs:/var/log/gitlab'
    - '~/Documents/gitlab/data:/var/opt/gitlab'
    - '~/Documents/gitlab/backups:/var/opt/gitlab/backups'

 

  • hostname : 설치할 서버의 IP 혹은 호스트명을 입력한다.
  • 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

아래의 공식 가이드 문서에 가면 본인이 원하는 이메일 설정 방법이 자세히 나와 있으므로 그것을 보고 따라하면 되겠다.

docs.gitlab.com/omnibus/settings/smtp.html

SMTP settings | GitLab

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 데이터가 복원된다.

docker exec -it <name of="" container=""> gitlab-rake gitlab:backup:restore

백업에 관련된 공식 문서는 아래의 링크를 참고하면 된다.

docs.gitlab.com/ee/raketasks/backup_restore.html#restore-for-docker-image-and-gitlab-helm-chart-installations

Back up and restore GitLab | 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

 

3-Tier container environment configuration using Kubernetes (NginX/Tomcat/MySQL)

쿠버네티스를 활용한 3-Tier 컨테이너 환경 구성을 해보고자 한다. 구성은 도커와 동일하게 아래와 같이 간다.

3-Tier container environment configuration using Docker(NginX/Tomcat/MySQL)

이번에는 구성 시에 1-Container에 WEB/WAS/DB Pod를 각각 띄울 예정이다.

동일한 컨테이너에서 기동하기 때문에 서로에 대한 IP는 변하지 않을 것이고 IP Address는 localhost로 설정해주어야 한다.

그렇기 때문에!!

– WEB-WAS 사이의 연동 설정에서 IP를 localhost로 변경

– WAS-DB 사이의 연동 설정(어플리케이션 부분)에서 DB IP를 localhost로 변경

해주어야한다.. 그렇다면 이미지 부터 다시 말아보자.

1. WEB 구성

1-1. NginX의 WAS 연동 설정 파일에서 IP Address를 localhost로 변경 후 다시 이미지 생성

> $ vi /root/nginx-for-petclinic/nginx/conf

17 http {
18     upstream was-list {
19         #server tomcat-petclinic:8080;
20         server localhost:8080;
21     }
22
23     include       mime.types;
24     default_type  application/octet-stream;

1-2. Dockerfile을 이용하여 이미지 생성

root@master:~/nginx-for-petclinic# pwd
/root/nginx-for-petclinic
root@master:~/nginx-for-petclinic# docker build -t nginx-petclinic:2.0 .

# 이미지 생성 후, 확인

root@master:~/nginx-for-petclinic# docker images
REPOSITORY                               TAG       IMAGE ID       CREATED         SIZE
nginx-petclinic                          2.0       1f34b3ccac6d   5 seconds ago   133MB

1-3. 이미지를 Dockerhub에 저장

> 내가 생성한 이미지를 이따 yaml파일에서 로드할  수 있도록..

root@master:~# docker tag nginx-petclinic:2.0 ghkd5216/nginx-petclinic:2.0
root@master:~# docker push ghkd5216/nginx-petclinic:2.0

The push refers to repository [docker.io/ghkd5216/nginx-petclinic]
52efdeec58ec: Pushed
201e7ba1790a: Pushed
9959a332cf6e: Layer already exists
f7e00b807643: Layer already exists
f8e880dfc4ef: Layer already exists
788e89a4d186: Layer already exists
43f4e41372e4: Layer already exists
e81bff2725db: Layer already exists
2.0: digest: sha256:15cf195c6e6575d744fa0eb07b429d0db3d99f61

2. WAS 구성

2-1. WAS에서는 AP쪽에 DB 설정이 있기 때문에 소스 빌드 및 해당 소스가 디플로이된 이미지를 다시 생성해야 한다.

> $vi /root/spring-framework-for-petclinic/pom.xml

> 515번 라인과 같이 localhost로 변경해주면 된다.

509             <id>MySQL</id>
510             <properties>
511                 <db.script>mysql</db.script>
512                 <jpa.database>MYSQL</jpa.database>
513                 <jdbc.driverClassName>com.mysql.jdbc.Driver</jdbc.driverClassName>
514                 <!–<jdbc.url>jdbc:mysql://mysql-petclinic:3306/petclinic?useUnicode=true</jdbc.url>–>
515                 <jdbc.url>jdbc:mysql://localhost:3306/petclinic?useUnicode=true</jdbc.url>
516                 <jdbc.username>root</jdbc.username>
517                 <jdbc.password>petclinic</jdbc.password>
518             </properties>

2-2. 소스 빌드를 위한 자바 변경

> 자바 버전이 1.8이 되도록 수정

root@master:~/spring-framework-for-petclinic# sudo update-java-alternatives –jre-headless –jre –set java-1.8.0-openjdk-amd64

root@master:~/spring-framework-for-petclinic# 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)

2-3. 소스 빌드

> 소스를 빌드하고 나면, 현 디렉토리에 target 안에 petclinic.war라는 소스 파일이 빌드되어 있을 것이다.

root@master:~/spring-framework-for-petclinic# mvn clean package -Dmaven.test.skip=true -P MySQL

…(중략)

[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time:  13.144 s
[INFO] Finished at: 2021-11-16T18:04:01+09:00
[INFO] ————————————————————————

2-4. WAS 이미지 생성

> 이제 만든 소스를 활용하여 tomcat 이미지를 만들어보도록 하자.

우선 이미지 생성을 위해 Dockerfile을 생성하고 위에서 만들었던 petclinic.war 파일을 Dockerfile 디렉토리에 위치시킨다.

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/

위와 같이 준비가 되었다면 이제 해당 위치에서 이미지 빌드를 하면 된다.

커맨드는 아래와 같다.

root@master:/home/dockerfile/petclinic# docker build -t tomcat-petclinic:2.0 .
Sending build context to Docker daemon  41.61MB
Step 1/2 : From tomcat:8-jre8
—> cff25871f024
Step 2/2 : ADD petclinic.war /usr/local/tomcat/webapps/
—> 1c6d007012d5
Successfully built 1c6d007012d5
Successfully tagged tomcat-petclinic:2.0

2-5. 이미지를 Dockerhub에 저장

> 내가 생성한 이미지를 이따 yaml파일에서 로드할  수 있도록..

root@master:~# docker tag tomcat-petclinic:2.0 ghkd5216/tomcat-petclinic:2.0
root@master:~# docker push ghkd5216/tomcat-petclinic:2.0
The push refers to repository [docker.io/ghkd5216/tomcat-petclinic]
3fe16f1fe077: Pushed
7a1149e8d0d8: Layer already exists
f3d15ade5c54: Layer already exists
80ac7083a323: Layer already exists
01de28a83f3f: Layer already exists
74a293db1084: Layer already exists
68c1e7509c65: Layer already exists
6ccd0e6bdf7a: Layer already exists
9f9f651e9303: Layer already exists
0b3c02b5d746: Layer already exists
62a747bf1719: Layer already exists
2.0: digest: sha256:7ee8909691df00b211aa42f1605062019422b0d93d52053d4ad01034390e04a7 size: 2631

3. YAML 파일 생성

이제 이미지도 준비되었고, yaml 파일만 생성하여 기동해보면 된다.
도커와는 다르게 쿠버네티스의 경우에는 쿠버네티스 클러스터 내에서 동작하기 때문에 생성된 컨테이너의 IP 또한 클러스터 내의 CNI 네트워크 대역으로 할당된다.

그렇기 때문에 외부에서 해당 컨테이너에 접근하여 서비스를 호출하려면 Service를 통해 가능하다. AWS나 GCP같은 Public Cloud의 경우에는 Service에서 LoadBalnancer를 제공하여 외부로 노출할 IP를 할당 받지만, 우리가 테스트하는 방식과 같이 Baremetal(On-Premise) 환경에서는 LoadBalancer 기능이 없다. 그래서 LoadBalancer타입을 지원하게끔 하기 위해 MetalLB라는 놈을 사용해주어야 한다.

먼저 metalb을 설치해보면 아래와 같다.

root@master:~/yaml# kubectl apply -f  https://raw.githubusercontent.com/google/metallb/v0.8.3/manifests/metallb.yaml

=================================
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 문법이나 파라미터 값을 공부해보아야 겠다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: petclinic-deployment
  namespace: default
  labels:
    app: petclinic-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
       app: petclinic-app
  template:
    metadata:
      labels:
        app: petclinic-app
    spec:
      containers:
      - image: mysql:5.7.8
        imagePullPolicy: Always
        name: mysql-petclinic
        ports:
        - containerPort: 3306
          protocol: TCP
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: petclinic
        - name: MYSQL_DATABASE
          value: petclinic
      - image: docker.io/ghkd5216/nginx-petclinic:2.0
        imagePullPolicy: Always
        name: nginx-petclinic
        ports:
        - containerPort: 80
          protocol: TCP
      - image: docker.io/ghkd5216/tomcat-petclinic:2.0
        imagePullPolicy: Always
        name: tomcat-petclinic
        env:
        - name: JAVA_OPTS
          value: "-Xms512m -Xmx512m"
        ports:
        - containerPort: 8080
          protocol: TCP
      restartPolicy: Always

---

apiVersion: v1
kind: Service
metadata:
  name: petclinic-svc
spec:
  type: LoadBalancer
  selector:
    app: petclinic-app
  ports:
    - port: 80
      protocol: TCP
      targetPort: 80


---

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 192.168.1.240-192.168.1.250

> 제일 하단에 metallb 관련 설정을 보면 addresses 범위 안에서 외부로 노출할 ExternalIP 대역을 설정할 수 있다.

이제 만든 yaml 파일을 실행하여 보자!!

4. Kubernetes를 통한 컨테이너 배포

root@master:~/yaml# kc apply -f petclinic-app.yaml
deployment.apps/petclinic-deployment created
service/petclinic-svc created
configmap/config created

root@master:~/yaml# kc get all
NAME                                        READY   STATUS    RESTARTS   AGE
pod/petclinic-deployment-86dc87794d-j69mm   3/3     Running   0          24s

NAME                    TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)        AGE
service/kubernetes      ClusterIP      10.96.0.1        <none>           443/TCP        13d
service/petclinic-svc   LoadBalancer   10.109.251.153   192.168.56.240   80:30560/TCP   24s

NAME                                   READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/petclinic-deployment   1/1     1            1           24s

NAME                                              DESIRED   CURRENT   READY   AGE
replicaset.apps/petclinic-deployment-86dc87794d   1         1         1       24s

> 위와 같이 LoadBalancer 타입의 Service가 생성되어 External-IP를 할당하여 외부로 노출하는 것을 볼 수 있다.

이제 모든 준비가 끝났다.

실제로 외부의 웹 브라우저에서 클라이언트 입장으로 서비스를 호출해보자. 호출 url은 EXTERNAL_IP:[LB PORT]/petclinic 이다.

나의 경우에는 http://192.168.56.240:80/petclinic/으로 호출하면 된다.

5. 서비스 호출

 

 

> 외부 노출 IP로 정상적으로 접근 가능하다. 몇 가지 업무 테스트를 해보면,

 

 

정상적으로 데이터가 DB에 저장되는 것을 볼 수 있다.

3-Tier container environment configuration using Docker(NginX/Tomcat/MySQL)

이번 글에서는 Docker를 사용하여 간단한 3-Tier 컨테이너 환경을 구성해보고자 한다.

– WEB = NginX

– WAS = Tomcat

– DB = MySQL

구성은 WAS -> WEB -> DB 순으로 하도록 한다.

우선,  WAS 부터 구성해보도록 하자.

1. WAS 구성

1-1. Github에 올라가 있는 어플리케이션 소스 다운로드

> 소스를 다운받을 디렉토리 생성 후 Git 저장소에서 소스를 Pull 한다.

root@master:~# mkdir -p /home/src
root@master:~# cd /home/src
root@master:/home/src#

root@master:/home/src# git clone https://github.com/Hwang-sangyeon/spring-framework-for-petclinic.git
Cloning into ‘spring-framework-for-petclinic’…
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 0), reused 4 (delta 0), pack-reused 0
Unpacking objects: 100% (4/4), 1.89 MiB | 4.96 MiB/s, done.

1-2. JDK와 Maven 설치

> 소스를 컴파일 하기 위해 OpenJDK와 Maven을 설치한다.

root@master:~# sudo apt-get install openjdk-8-jdk maven

(중략)

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# sudo update-java-alternatives –list
java-1.11.0-openjdk-amd64      1111       /usr/lib/jvm/java-1.11.0-openjdk-amd64
java-1.8.0-openjdk-amd64       1081       /usr/lib/jvm/java-1.8.0-openjdk-amd64
root@master:/home/src#
root@master:/home/src#
root@master:~# sudo update-java-alternatives –jre-headless –jre –set java-1.8.0-openjdk-amd64

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/

위와 같이 준비가 되었다면 이제 해당 위치에서 이미지 빌드를 하면 된다.

커맨드는 아래와 같다.

root@master:~/docker/tomcat-test# docker build -t tomcat-petclinic .
Sending build context to Docker daemon  41.61MB
Step 1/2 : From tomcat:8-jre8
8-jre8: Pulling from library/tomcat
bb7d5a84853b: Pull complete
f02b617c6a8c: Pull complete
d32e17419b7e: Pull complete
ab18cfab55f9: Pull complete
793716e93ecb: Pull complete
3b9b14f7678b: Pull complete
4dc2d594b57d: Pull complete
3f275ee15a3d: Pull complete
15b0db619216: Pull complete
c0aa838e7a3a: Pull complete
Digest: sha256:47165dabe7c092c61ef2726a7fdc70dda0ce9dea07ca68bda0aea6ecfaba2873
Status: Downloaded newer image for tomcat:8-jre8
—> cff25871f024
Step 2/2 : ADD petclinic.war /usr/local/tomcat/webapps/
—> f27039d77fc4
Successfully built f27039d77fc4
Successfully tagged tomcat-petclinic:latest

2. WEB 구성

2-1. NginX 리소스 다운로드

> Git에 올라가 있는 NginX 리소스를 다운받는다.

root@master:/home/src# git clone https://github.com/Hwang-sangyeon/nginx-for-petclinic.git
Cloning into ‘nginx-for-petclinic’…

리소스 다운로드 후,

nginx.conf 파일에서 was 연동 포트 확인 및 nginx listen-port를 확인한다.

2-2. 이미지 생성

> Dockerfile이 있는 디렉토리에서 이미지를 빌드한다.

root@master:/home/src/nginx-for-petclinic/nginx-for-petclinic# docker build -t nginx-petclinic .
Sending build context to Docker daemon  6.656kB

2-3. WEB/WAS 이미지 확인

root@master:/home/src/nginx-for-petclinic/nginx-for-petclinic# docker images|grep petclinic
nginx-petclinic                          latest    952aff9083ee   19 seconds ago   133MB
tomcat-petclinic                         latest    39e56a91e5e7   9 minutes ago    334MB

3. 컨테이너 기동

이제부터는 필요한 이미지가 준비되었기 때문에 WEB/WAS/DB 컨테이너를 기동하여 3-Tier를 구성을 해보려고 한다.

여기서 중요한 점은 WAS가 기동되기 전에 (소스 빌드) DB가 먼저 떠있어야 하기 때문에 DB 컨테이너를 반드시 먼저 기동시켜야 한다는 것이다.

3-1. DB 컨테이너 기동

> mysql-petclinic 컨테이너 명으로 mysql 이미지를 바로 받아 컨테이너를 실행한다.

docker run --name mysql-petclinic -e MYSQL_ROOT_PASSWORD=petclinic -e MYSQL_DATABASE=petclinic -p 3306:3306 mysql:5.7.8

Initializing database 이후 docker container ps로 확인

root@master:~# docker container ps | grep mysql
d286f5c59a6c   mysql:5.7.8            “/entrypoint.sh mysq…”   57 seconds ago      Up 56 seconds      0.0.0.0:3306->3306/tcp, :::3306->3306/tcp   mysql-petclinic

3-2. WAS 컨테이너 기동

docker run -it --rm -p 8080:8080 --name tomcat-petclinic --link mysql-petclinic:mysql-petclinic tomcat-petclinic

> tomcat 컨테이너에서 mysql 컨테이너의 ip로 접속할 수 있겠지만, static ip가 아니기 때문에 mysql 컨테이너의 이름으로 접속을 해야 할 필요가 있다. 이럴 경우 link 옵션을 통해 컨테이너를 구동하면 tomcat 컨테이너에서 mysql 컨테이너로 이름으로 접속할 수 있다. link 옵션으로 구동하면 톰캣 컨테이너 안의 /etc/hosts에 링크된 mysql 컨테이너 hostname과 ip가 들어간다.

3-3. WEB 컨테이너 기동

docker run -it --rm -p 80:80 --name nginx-petclinic --link tomcat-petclinic:tomcat-petclinic nginx-petclinic

4. 서비스 호출

 

192.168.56.200은 VM의 IP

 

이상 도커를 활용하여 3-Tier 컨테이너 환경을 구성해보았다..

Kubernetes Monitoring : HPA (Horizontal Pod Autoscaler)

Overview

쿠버네티스 클러스터에서 hpa를 적용해 시스템 부하상태에 따라 pod을 autoScaling시키는 실습을 진행하겠습니다.

참고 링크 : Kubernetes.io/Horizontal Pod Autoscaler

Prerequisites

먼저 쿠버네티스 클러스터를 생성해주세요.

참고링크 : 호롤리한하루/Install Kubernetes on CentOS/RHEL

본 실습에서 사용한 spec :
OS : CentOS v7.6
Arch : x86

Kubernetes : v1.16.2
Master : 4cpu, ram16G (1개)
Node : 4cpu, ram16G (2개)

Step

그림1

1. Metrics-Server 배포

먼저 top명령어를 입력해봅시다.

$ kubectl top node

Error from server (NotFound): the server could not find the requested resource (get services http:heapster:)

현재는 에러메세지가 뜹니다.
이유는 노드의 system metric들을 수집하는 서비스가 없기 때문입니다.

system metric을 수집하는 Metrics-Server를 배포해주도록 합시다.

$ git clone https://github.com/kubernetes-sigs/metrics-server.git
$ cd metrics-server

쿠버네티스에서 공식으로 서포트하고있는 add-on 컴포넌트인 metrics-server를 클론받고, 배포하기 이전에 yaml파일을 수정해주어야합니다.
클러스터 내에서 사용하는 인증서가 신뢰되지 않은 경우와 호스트 이름을 찾지 못하는 경우를 방지하기 위함입니다

$ vim deploy/kubernetes/metrics-server-deployment.yaml

image
위 그림과 같이 argument들을 수정해줍니다.

args:
  - --kubelet-insecure-tls
  - --kubelet-preferred-address-types=InternalIP
  - --cert-dir=/tmp
  - --secure-port=4443

그리고 나서 metrics-server를 배포하면 :

$ 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에 등록된 것을 확인할 수 있습니다.

이제 top명령어를 사용할 수 있게 됩니다.

$ kubectl top nodes

NAME       CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
kube-m     248m         6%     1656Mi          10%
kube-n01   112m         2%     760Mi           4%
kube-n02   112m         2%     724Mi           4%

각 노드의 cpu와 memory사용량을 확인할 수 있습니다.

2. 부하테스트를 위한 이미지 작성

이제 시스템의 부하테스트를 위한 이미지를 작성해보겠습니다.

$ mkdir php
cd php

스크립트 작성 :

$ vim index.php

<?php
  $x = 0.0001;
  for ($i = 0; $i <= 1000000; $i++) {
    $x += sqrt($x);
  }
  echo "OK!";
?>

스크립트를 포함하는 도커 이미지 작성 :

$ vim Dockerfile

FROM php:5-apache
ADD index.php /var/www/html/index.php
RUN chmod a+rx index.php

도커 이미지 빌드 :

$ docker build --tag {docker id : totoli78}/php-apache .

$ docker images |grep php

totoli78/php-apache                    latest              39e1797ad29c        23 seconds ago      355MB

생성한 이미지를 본인의 docker hub에 push해줍니다.

$ docker login
...
Login Succeeded

$ docker push totoli78/php-apache

부하테스트를 위해 쿠버네티스 클러스터에 pod으로 배포해줍니다.

$ vim hpa-test.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: totoli78/php-apache
        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

yaml을 작성한 뒤에는 배포!

$ kubectl apply -f hpa-test.yaml

deployment.apps/php-apache created
service/php-apache created

3. HPA 배포

이제 오토스케일러를 생성해주면 됩니다.

$ vim autoscaler.yaml

위에서 만든 부하테스트용 pod인 php-apache의 평균 cpu사용량을 50%로 맞추기 위해 레플리카의 개수를 늘리고 줄입니다. (1개~10개)

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50
$ 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

replica autoscaling은 몇 분 정도 소요됩니다. (체감상 3~5분)

[kubernetes] kubeadm 사용 고 가용성(HA) 클러스터 설정

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

 

첫번째 Control plane node

1. Control plane 초기화

shell> sudo kubeadm init –control-plane-endpoint apisvr.hoya.com:6443 –upload-certs

** apisvr.hoya.com:6443 은 자기 환경에 맞게 설정한다.

–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

참고: certificate-key를 지정하여 나중에 조인에서 사용할 수 있습니다.

shell> sudo kubeadm alpha certs certificate-key
f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07

참고: worker node에서 토큰을 사용하여 클러스터에 가입하는 데 필요한 전체 ‘kubeadm join’ 플래그를 출력

shell> sudo kubeadm token create –print-join-command

참고: 인증서를 다시 업로드하고 새 암호 해독 키를 생성하려면 이미 클러스터에 연결된 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> sudo kubeadm token create –print-join-command –certificate-key \  7f97aaa65bfec1f039c4dbdf3a2073de853c708bd4d9ff9d72b776b0f9874c9d

2. CNI(Container Network Interface) 플러그인 설치

– 여기서는 weave CNI 플러그인 설치(그외 flannel, Calico, ….)

shell> kubectl apply -f “https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d ‘\n’)”

 

나머지 Control plane 노드

1. 첫번째 Control plane 노드에서 kubeadm init 결과(파란색 글씨)를 실행한다.

shell> kubeadm join apisvr.hoya.com:6443 –token 9vr73a.a8uxyaju799qwdjv –discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 –control-plane –certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07

2. 확인

– 노드 상태가 Ready인지 확인

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>

– pods의 상태(Status)가 모두 Running 상태인지 확인

shell> kubectl get pods –all-namespaces
NAMESPACE     NAME                                READY   STATUS    RESTARTS   AGE
kube-system   coredns-6955765f44-7jk6m            1/1     Running   0          3m42s
kube-system   coredns-6955765f44-vgw2j            1/1     Running   0          3m42s
kube-system   etcd-master3                       1/1     Running   0          31s
kube-system   etcd-master1                       1/1     Running   0          3m44s
kube-system   etcd-master2                      1/1     Running   0          83s
kube-system   kube-apiserver-master3             1/1     Running   0          33s
kube-system   kube-apiserver-master1             1/1     Running   0          3m44s
kube-system   kube-apiserver-master2            1/1     Running   0          84s
kube-system   kube-controller-manager-master3    1/1     Running   0          33s
kube-system   kube-controller-manager-master1    1/1     Running   1          3m44s
kube-system   kube-controller-manager-master2   1/1     Running   0          84s
kube-system   kube-proxy-hd8vq                    1/1     Running   0          84s
kube-system   kube-proxy-v9s8h                    1/1     Running   0          3m42s
kube-system   kube-proxy-z76td                    1/1     Running   0          33s
kube-system   kube-scheduler-master3             1/1     Running   0          33s
kube-system   kube-scheduler-master1             1/1     Running   1          3m44s
kube-system   kube-scheduler-master2            1/1     Running   0          84s
kube-system   weave-net-6dkt6                     2/2     Running   3          84s
kube-system   weave-net-9zswx                     2/2     Running   0          3m3s
kube-system   weave-net-fvf9q                     2/2     Running   0          33s

woker node

1. 첫번째 Control plane 노드에서 kubeadm init 결과(주황색 글씨)를 실행한다.

kubeadm join apisvr.hoya.com:6443 –token 9vr73a.a8uxyaju799qwdjv –discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866

 

HA에서 control plane 노드 제거

 방법 1) 제거 할 노드에서 아래 명령어(kubeadm reset) 실행

shell> sudo kubeadm reset

** 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

 방법 2) etcd member 삭제 진행 절차

  step 1. 유효한 control plane 노드에서 etcd member id 확인

shell> etcdctl –cacert=/etc/kubernetes/pki/etcd/ca.crt \ 
 –cert=/etc/kubernetes/pki/etcd/peer.crt \ 
 –key=/etc/kubernetes/pki/etcd/peer.key  member list 81ce68f24db8ced, started, master1, https://192.168.0.159:2380, https://192.168.0.159:2379, false
5364415ff53d0975, started, master3, https://192.168.0.158:2380, https://192.168.0.158:2379, false
b27f6573a770c244, started, master2, https://192.168.0.149:2380, https://192.168.0.149:2379, false
shell>

  step 2. 멤버 제거

shell> etcdctl -cacert=/etc/kubernetes/pki/etcd/ca.crt  \ 
–cert=/etc/kubernetes/pki/etcd/peer.crt \  
–key=/etc/kubernetes/pki/etcd/peer.key member remove 81ce68f24db8cedMember  81ce68f24db8ced removed from cluster  88707d65fd36eb2
shell>

 

TroubleShooting

참고 : 아래 환경에서 etcdctl 을 이용하여 접속하면 오류가 발생하여 etcd 버전을 업그레이드(v3.4.5)하여 진행, 아마 etcd, etcdctl 은 마이너 버전까지 일치해야만 정상 작동하는듯 하다.

– kubernetes etcd 버전 : 3.4.3-0

– etcdctl 버전

shell> etcdctl –version
etcdctl version: 3.2.10
API version: 2

 

증상 1) etcdctl version 3.2.10 으로 실행 오류 #1 – 404 오류 발생

shell> etcdctl -C https://192.168.0.149:2379 –ca-file=/tmp/pki/ca.crt  \
–cert-file=/tmp/pki/peer.crt  \
–key-file=/tmp/pki/peer.key member list
unexpected status code 404
shell>

증상 2) etcdctl version 3.2.10 으로 실행시 오류 #2( /etc/kubernetes/pki/etcd 디폴트 위치한 인증서 사용시)

shell> etcdctl -C https://192.168.0.149:2379 –ca-file=/etc/kubernetes/pki/etcd/ca.crt  \
–cert-file=/etc/kubernetes/pki/etcd/peer.crt  \
–key-file=/etc/kubernetes/pki/etcd/peer.key member list
open /etc/kubernetes/pki/etcd/peer.crt: permission denied
shell>

[Docker SWARM] Configuring a cluster environment using SWARM

manager : docker swarm 클러스터의 manager 노드

worker01, worker02, worker03 : docker swarm의 worker노드들 (3개)

registry : docker private registry 서비스

registry-web : docker private registry에 어떤 이미지가 올라가 있는지 web UI로 확인하는 서비스

dashboard : docker swarm 클러스터의 노드들을 web UI로 확인할 수 있는 서비스

 

대충 최종 모습(?)

 

준비사항

$ docker -v
Docker version 20.10.5, build 55c4c88

docker가 설치되어 있어야 한다.

$ docker-compose -v
docker-compose version 1.28.5, build c4eb3a1f

docker-compose도 설치되어 있어야 한다.

서비스 파일 작성

– docker-compose.yml

$ cat docker-compose.yml
version: "3"
services:
  registry-web:
    container_name: registry-web
    image: hyper/docker-registry-web
    ports:
      - 8080:8080
    volumes:
      - "./config.yml:/conf/config.yml:ro"

  registry:
    container_name: registry
    image: registry:2.6
    ports:
      - 5000:5000
    volumes:
      - "./registry-data:/var/lib/registry"

  manager:
    container_name: manager
    image: docker:18.05.0-ce-dind
    privileged: true
    tty: true
    ports:
      - 8000:80
      - 9000:9000
      - 8081:8081
      - 4567:4567
    depends_on:
      - registry
    expose:
      - 3375
    command: "--insecure-registry registry:5000"
    volumes:
      - "./stack:/stack"
      - "./dashboard:/dashboard"

  worker01:
    container_name: worker01
    image: docker:18.05.0-ce-dind
    privileged: true
    tty: true
    depends_on:
      - manager
      - registry
    expose:
      - 7946
      - 7946/udp
      - 4789/udp
    command: "--insecure-registry registry:5000"

  worker02:
    container_name: worker02
    image: docker:18.05.0-ce-dind
    privileged: true
    tty: true
    depends_on:
      - manager
      - registry
    expose:
      - 7946
      - 7946/udp
      - 4789/udp
    command: "--insecure-registry registry:5000"

  worker03:
    container_name: worker03
    image: docker:18.05.0-ce-dind
    privileged: true
    tty: true
    depends_on:
      - manager
      - registry
    expose:
      - 7946
      - 7946/udp
      - 4789/udp
    command: "--insecure-registry registry:5000"

manager와 worker 노드들은 dind(docker in docker) 이미지로 생성한다. dind는 간단히 말하자면 docker 컨테이너 안에서 docker cli를 사용하는 것이다.

registry와 registry를 web UI로 보여주는 서비스들을 추가한다. registry-web은 8080 포트로 접속할 것이다.

– config.yml

$ cat config.yml
registry:
  # 기존에 설치한 docker private registry
  url: http://registry:5000/v2
  # Docker registry name
  name: localhost:5000
  # docker 권한 부여
  readonly: false
  auth:
  eabled: false

registry-web에 사용될 yml 파일이다. private registry가 사용하는 5000번 포트로 url을 설정한다.

– dashbaord.yml

$ cat dashboard/dashboard.yml
version: "3"

services:
  dashboard:
    image: charypar/swarm-dashboard
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    ports:
      - 8081:8081
    environment:
      PORT: 8081
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager

docker-compose.yml 파일을 보면 manager 노드에 /dashboard 디렉토리를 마운트 해주었는데 즉, docker-compose.yml이 있는 곳에 dashboard라는 디렉토리를 만들고, 그 안에 dashboard.yml을 작성하였다.

dashboard 서비스는 manager 노드에만 생성되고, 브라우저에서 8081포트로 접속한다.

swarm-dashboard에 대한 github 주소는 아래와 같다. 개발자 분에게 감사합니다.

github.com/charypar/swarm-dashboard

– 디렉토리 상태

$ ls
config.yml  dashboard  docker-compose.yml

dashboard만 디렉토리이고, dashboard 디렉토리 안에 dashboard.yml 파일이 있다.

docker compose 실행

– docker-compose up -d

$ docker-compose up -d
Creating network "swarm_default" with the default driver
Creating registry     ... done
Creating registry-web ... done
Creating manager      ... done
Creating worker01     ... done
Creating worker03     ... done
Creating worker02     ... done

-d 옵션은 백그라운드로 시작하는 뜻이다.

– 디렉토리 재확인

$ 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 image push

$ docker image push localhost:5000/example/whoami:latest
The push refers to repository [localhost:5000/example/whoami]
6304fb0017b0: Pushed
bcd68c905028: Pushed
5f4ed2a4afd7: Pushed
1fad3fef68ba: Pushed
42e63b663df9: Pushed
71d7318763a9: Pushed
7d7e183520a5: Pushed
7cbcbac42c44: Pushed
latest: digest: sha256:6239cd2462f9dd7a0317db107724a101deca600d30e39465515ba632e0982f4a size: 1989

이미지가 푸쉬되는걸 확인할 수 있다.

– registry web 확인

 

registry-web

 

이미지가 올라간것을 확인할 수 있다. 이제 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.

Swarm dashboard 서비스 실행

– swarm dashboard stack deploy

$ docker exec -it manager docker stack deploy -c /dashboard/dashboard.yml dashboard
Creating network dashboard_default
Creating service dashboard_dashboard

컨테이너에 마운트해둔 /dashboard/dashboard.yml을 사용해 dashboard서비스를 실행한다.

– dashboard 접속

 

dashboard 접속

 

localhost:8081로 접속하면, 위처럼 현재 Swarm cluster의 상태를 확인할 수 있다. 현재 manager 노드에 dashboard 서비스 한개만 올라가 있는 상태이다.

Swarm 클러스터에 서비스 등록

private registry에 올려놨던 image를 manager 노드에서 pull 받아서 swarm 클러스에서 서비스로 등록해본다.

– docker pull image

$ docker exec -it manager docker pull registry:5000/example/whoami:latest
latest: Pulling from example/whoami
d1426d011624: Pull complete
1659ef4c811e: Pull complete
47bd5f3578fc: Pull complete
5e03057c6ddf: Pull complete
d58f420d5777: Pull complete
d65d30e11c7f: Pull complete
c9d3f35ab05f: Pull complete
fb24e6aeba3f: Pull complete
Digest: sha256:6239cd2462f9dd7a0317db107724a101deca600d30e39465515ba632e0982f4a
Status: Downloaded newer image for registry:5000/example/whoami:latest

– docker service create

$ docker exec -it manager docker service create --name whoami -p 4567:4567 registry:5000/example/whoami:latest
wkkg33d93qity2dustm8dkxvf
overall progress: 1 out of 1 tasks
1/1: running   [==================================================>]
verify: Service converged

– docker service 확인

$ docker exec -it manager docker service ls
ID                  NAME                  MODE                REPLICAS            IMAGE                                 PORTS
isro3osqfsn5        dashboard_dashboard   replicated          1/1                 charypar/swarm-dashboard:latest       *:8081->8081/tcp
wkkg33d93qit        whoami                replicated          1/1                 registry:5000/example/whoami:latest   *:4567->4567/tcp

dashboard 서비스와 whoami 서비스를 확인할 수 있다.

 

 

dashboard에서도 whoami 서비스 하나가 추가된 것을 확인할 수 있다.

– service scale 조절

$ docker exec -it manager docker service scale whoami=6
whoami scaled to 6
overall progress: 6 out of 6 tasks
1/6: running   [==================================================>]
2/6: running   [==================================================>]
3/6: running   [==================================================>]
4/6: running   [==================================================>]
5/6: running   [==================================================>]
6/6: running   [==================================================>]
verify: Service converged

docker service scale 명령어로 서비스의 스케일링을 간단하게 진행할 수 있다. whoami 서비스가 원래 1개였는데, 6개로 늘렸다.

 

 

dashboard에서도 whoami 서비스가 6개로 증가한 것을 확인할 수 있다.

curl test

$ curl localhost:4567
444593584a03
$ curl localhost:4567
fb1e399ac4ed
$ curl localhost:4567
a8948b879833
$ curl localhost:4567
f565b65a22ab
$ curl localhost:4567
7160d9d9e250
$ curl localhost:4567
d7c6861c57e0
$ curl localhost:4567
444593584a03
$ curl localhost:4567
fb1e399ac4ed
$ curl localhost:4567
a8948b879833
$ curl localhost:4567
f565b65a22ab
$ curl localhost:4567
7160d9d9e250
$ curl localhost:4567
d7c6861c57e0

노드들의 hostname이 적절히 분배되어 출력되는걸 확인할 수 있다. docker swarm의 경우 ingress 네트워크가 트래픽 분산을 자동으로 해준다.

kubeadm 이용 Ubuntu 20.04에 Kubernetes 클러스터 설치

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 배포에 사용할 서버를 프로비저닝합니다. 설정 프로세스는 사용 중인 가상화 또는 클라우드 환경에 따라 다릅니다.

서버가 준비되면 업데이트하십시오.

sudo apt update
sudo apt -y upgrade && sudo systemctl reboot

2단계: kubelet, beadm 및 kubectl 설치

서버가 재부팅되면 Ubuntu 20.04용 Kubernetes 저장소를 모든 서버에 추가하십시오.

sudo apt update
sudo apt -y install curl apt-transport-https
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list

그런 다음 필요한 패키지를 설치합니다.

sudo apt update
sudo apt -y install vim git curl wget kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

kubectl 버전을 확인하여 설치를 확인합니다.

$ kubectl version --client && kubeadm version
Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.2", GitCommit:"8b5a19147530eaac9476b0ab82980b4088bbc1b2", GitTreeState:"clean", BuildDate:"2021-09-15T21:38:50Z", GoVersion:"go1.16.8", Compiler:"gc", Platform:"linux/amd64"}
kubeadm version: &version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.2", GitCommit:"8b5a19147530eaac9476b0ab82980b4088bbc1b2", GitTreeState:"clean", BuildDate:"2021-09-15T21:37:34Z", GoVersion:"go1.16.8", Compiler:"gc", Platform:"linux/amd64"}

3단계: 스왑 비활성화

스왑과 방화벽을 끕니다.

sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
sudo swapoff -a
sudo ufw disable

crontab -e
@reboot sudo swapoff -a 

커널 모듈을 활성화하고 sysctl을 구성합니다.

# Enable kernel modules
sudo modprobe overlay
sudo modprobe br_netfilter

# Add some settings to sysctl
sudo tee /etc/sysctl.d/kubernetes.conf<<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF

# Reload sysctl
sudo sysctl --system

4단계: 컨테이너 런타임 설치

Pod에서 컨테이너를 실행하기 위해 Kubernetes는 컨테이너 런타임을 사용합니다. 지원되는 컨테이너 런타임은 다음과 같습니다.

  • 도커
  • CRI-O
  • 컨테이너

참고 : 한 번에 하나의 런타임을 선택해야 합니다 .

도커 런타임 설치:

# Add repo and Install packages
sudo apt update
sudo apt install -y curl gnupg2 software-properties-common apt-transport-https ca-certificates

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

sudo apt update
sudo apt install -y containerd.io docker-ce docker-ce-cli

# Create required directories
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo mkdir -p /etc/docker

# Create daemon json config file
sudo tee /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF

# Start and enable Services
sudo systemctl daemon-reload 
sudo systemctl restart docker
sudo systemctl enable docker

CRI-O 설치:

# Ensure you load modules
sudo modprobe overlay
sudo modprobe br_netfilter

# Set up required sysctl params
sudo tee /etc/sysctl.d/kubernetes.conf<<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF

# Reload sysctl
sudo sysctl --system

# Add Cri-o repo
sudo su -
OS="xUbuntu_20.04"
VERSION=1.22
echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/ /" > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
echo "deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$VERSION/$OS/ /" > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:$VERSION.list
curl -L https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:$VERSION/$OS/Release.key | apt-key add -
curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/Release.key | apt-key add -

# Update CRI-O CIDR subnet
sudo sed -i 's/10.85.0.0/192.168.0.0/g' /etc/cni/net.d/100-crio-bridge.conf

# Install CRI-O
sudo apt update
sudo apt install cri-o cri-o-runc

# Start and enable Service
sudo systemctl daemon-reload
sudo systemctl restart crio
sudo systemctl enable crio
sudo systemctl status crio

컨테이너 설치:

# Configure persistent loading of modules
sudo tee /etc/modules-load.d/containerd.conf <<EOF
overlay
br_netfilter
EOF

# Load at runtime
sudo modprobe overlay
sudo modprobe br_netfilter

# Ensure sysctl params are set
sudo tee /etc/sysctl.d/kubernetes.conf<<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF

# Reload configs
sudo sysctl --system

# Install required packages
sudo apt install -y curl gnupg2 software-properties-common apt-transport-https ca-certificates

# Add Docker repo
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

# Install containerd
sudo apt update
sudo apt install -y containerd.io

# Configure containerd and start service
sudo su -
mkdir -p /etc/containerd
containerd config default>/etc/containerd/config.toml

# restart containerd
sudo systemctl restart containerd
sudo systemctl enable containerd
systemctl status  containerd

 cgroup 드라이버 설치

systemd cgroup 드라이버를 사용하려면 에서 plugins.cri.systemd_cgroup = true 를 설정 /etc/containerd/config.toml하십시오. kubeadm을 사용할 때 kubelet 용 cgroup 드라이버를 수동으로 구성하십시오.

5단계: 마스터 노드 초기화

마스터로 사용할 서버에 로그인하고 br_netfilter 모듈이 로드되었는지 확인합니다.

$ lsmod | grep br_netfilter
br_netfilter           22256  0 
bridge                151336  2 br_netfilter,ebtable_broute

컨테이너 이미지 가져오기:

$ sudo kubeadm config images pull
[config/images] Pulled k8s.gcr.io/kube-apiserver:v1.22.2
[config/images] Pulled k8s.gcr.io/kube-controller-manager:v1.22.2
[config/images] Pulled k8s.gcr.io/kube-scheduler:v1.22.2
[config/images] Pulled k8s.gcr.io/kube-proxy:v1.22.2
[config/images] Pulled k8s.gcr.io/pause:3.5
[config/images] Pulled k8s.gcr.io/etcd:3.5.0-0
[config/images] Pulled k8s.gcr.io/coredns/coredns:v1.8.4

kubelet 서비스를 활성화합니다.

sudo systemctl enable kubelet

이제 etcd (클러스터 데이터베이스)와 API 서버를 포함하는 컨트롤 플레인 구성 요소를 실행할 시스템을 초기화하려고 합니다.

 

CRI 소켓이 여러 개인 경우 다음 --cri-socket중 하나를 선택 하는 데 사용하십시오.

# CRI-O
sudo kubeadm config images pull --cri-socket /var/run/crio/crio.sock

# Containerd
sudo kubeadm config images pull --cri-socket /run/containerd/containerd.sock

# Docker
sudo kubeadm config images pull --cri-socket /var/run/dockershim.sock

kubeadm init클러스터를 부트스트랩하는 데 사용되는 기본 옵션입니다.

--control-plane-endpoint : 모든 제어 평면 노드에 대한 공유 끝점을 설정합니다. DNS/IP일 수 있음
 --pod-network-cidr : 포드 네트워크 추가 기능을 설정하는 데 사용됨 CIDR
 --cri-socket : 런타임 소켓 경로를 설정하기 위해 컨테이너 런타임이 둘 이상인 경우 사용
 --apiserver-advertise-address : 이 특정 제어 평면 노드의 API 서버에 대한 광고 주소 설정

공유 엔드포인트가 없는 부트스트랩 (Worker 초기 설정 시 진행)

DNS 엔드포인트를 사용하지 않고 클러스터를 부트스트랩하려면 다음을 실행합니다.

sudo kubeadm init \
  --pod-network-cidr=192.168.0.0/16

공유 엔드포인트가 있는 부트스트랩(제어 평면 API의 DNS 이름)

클러스터 엔드포인트 DNS 이름을 설정하거나 /etc/hosts 파일에 레코드를 추가합니다.

$ sudo vim /etc/hosts
172.29.20.5 k8s-cluster.computingforgeeks.com

클러스터 생성: (Master Node 경우)

sudo kubeadm init \
  --pod-network-cidr=192.168.0.0/16 \
  --upload-certs \
  --control-plane-endpoint=k8s-cluster.computingforgeeks.com

참고 : 192.168.0.0/16 이 이미 네트워크 내에서 사용 중인 경우 위 명령에서 192.168.0.0/16을 대체하여 다른 포드 네트워크 CIDR을 선택해야 합니다.

컨테이너 런타임 소켓:

실행 시간 Unix 도메인 소켓 경로
도커 /var/run/docker.sock
용기에 담긴 /run/containerd/containerd.sock
만들기 /var/run/crio/crio.sock

선택적으로 런타임용 소켓 파일을 전달하고 설정에 따라 주소를 알릴 수 있습니다.

# CRI-O
sudo kubeadm init \
  --pod-network-cidr=192.168.0.0/16 \
  --cri-socket /var/run/crio/crio.sock \
  --upload-certs \
  --control-plane-endpoint=k8s-cluster.computingforgeeks.com

# Containerd
sudo kubeadm init \
  --pod-network-cidr=192.168.0.0/16 \
  --cri-socket /run/containerd/containerd.sock \
  --upload-certs \
  --control-plane-endpoint=k8s-cluster.computingforgeeks.com

# Docker
sudo kubeadm init \
  --pod-network-cidr=192.168.0.0/16 \
  --cri-socket /var/run/docker.sock \
  --upload-certs \
  --control-plane-endpoint=k8s-cluster.computingforgeeks.com

다음은 초기화 명령의 출력입니다.

....
[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을 구성합니다.

mkdir -p $HOME/.kube
sudo cp -f /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

echo 'export KUBECONFIG=$HOME/.kube/config' >> $HOME/.bashrc

클러스터 상태 확인:

$ 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'.

설치 출력의 명령을 사용하여 마스터 노드를 추가할 수 있습니다.

kubeadm join k8s-cluster.computingforgeeks.com:6443 --token sr4l2l.2kvot0pfalh5o4ik \
    --discovery-token-ca-cert-hash sha256:c692fb047e15883b575bd6710779dc2c5af8073f7cab460abd181fd3ddb29a18 \
    --control-plane 

6단계: 마스터에 네트워크 플러그인 설치

이 가이드에서는 Calico 를 사용 합니다. 지원되는 다른 네트워크 플러그인 을 선택할 수 있습니다 .

kubectl create -f https://docs.projectcalico.org/manifests/tigera-operator.yaml 
kubectl create -f https://docs.projectcalico.org/manifests/custom-resources.yaml

다음 출력이 표시되어야 합니다.

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

모든 포드가 실행 중인지 확인합니다.

$ watch kubectl get pods --all-namespaces
NAMESPACE     NAME                                                         READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-76d4774d89-nfqrr                     1/1     Running   0          2m52s
kube-system   calico-node-kpprr                                            1/1     Running   0          2m52s
kube-system   coredns-66bff467f8-9bxgm                                     1/1     Running   0          7m43s
kube-system   coredns-66bff467f8-jgwln                                     1/1     Running   0          7m43s
kube-system   etcd-k8s-master01.computingforgeeks.com                      1/1     Running   0          7m58s
kube-system   kube-apiserver-k8s-master01.computingforgeeks.com            1/1     Running   0          7m58s
kube-system   kube-controller-manager-k8s-master01.computingforgeeks.com   1/1     Running   0          7m58s
kube-system   kube-proxy-bt7ff                                             1/1     Running   0          7m43s
kube-system   kube-scheduler-k8s-master01.computingforgeeks.com            1/1     Running   0          7m58s

마스터 노드가 준비되었는지 확인합니다.

# CRI-O
$ kubectl get nodes -o wide
NAME     STATUS   ROLES                  AGE   VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION     CONTAINER-RUNTIME
ubuntu   Ready    control-plane,master   38s   v1.22.2   143.198.114.46   <none>        Ubuntu 20.04.3 LTS   5.4.0-88-generic   cri-o://1.22.0

# Containerd
$ kubectl get nodes -o wide
NAME     STATUS   ROLES                  AGE   VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION     CONTAINER-RUNTIME
ubuntu   Ready    control-plane,master   15m   v1.22.2   143.198.114.46   <none>        Ubuntu 20.04.3 LTS   5.4.0-88-generic   containerd://1.4.11

# Docker
$ kubectl get nodes -o wide
NAME           STATUS   ROLES    AGE   VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE           KERNEL-VERSION     CONTAINER-RUNTIME
k8s-master01   Ready    master   64m   v1.22.2   135.181.28.113   <none>        Ubuntu 20.04 LTS   5.4.0-37-generic   docker://20.10.8

7단계: 작업자 노드 추가

제어 플레인이 준비되면 예약된 워크로드를 실행하기 위해 클러스터에 작업자 노드를 추가할 수 있습니다.

엔드포인트 주소가 DNS에 없으면 /etc/hosts 에 레코드를 추가 합니다 .

$ sudo vim /etc/hosts
172.29.20.5 k8s-cluster.computingforgeeks.com

주어진 조인 명령은 클러스터에 작업자 노드를 추가하는 데 사용됩니다.

kubeadm join k8s-cluster.computingforgeeks.com:6443 \
  --token sr4l2l.2kvot0pfalh5o4ik \
  --discovery-token-ca-cert-hash sha256:c692fb047e15883b575bd6710779dc2c5af8073f7cab460abd181fd3ddb29a18

산출:

[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

조인 토큰이 만료된 경우 작업자 노드에 조인하는 방법에 대한 가이드를 참조하십시오.

8단계: 클러스터에 애플리케이션 배포

단일 노드 클러스터만 있는 경우 마스터 노드에서 컨테이너 포드를 실행하는 방법에 대한 가이드를 확인하세요.

애플리케이션을 배포하여 클러스터가 작동하는지 확인해야 합니다.

kubectl apply -f https://k8s.io/examples/pods/commands.yaml

포드가 시작되었는지 확인

$ kubectl get pods
NAME           READY   STATUS      RESTARTS   AGE
command-demo   0/1     Completed   0          16s

9단계: Kubernetes 대시보드 설치(선택 사항)

Kubernetes 대시보드는 컨테이너화된 애플리케이션을 Kubernetes 클러스터에 배포하고, 컨테이너화된 애플리케이션의 문제를 해결하고, 클러스터 리소스를 관리하는 데 사용할 수 있습니다.

설치 가이드를 참조하십시오: NodePort로 Kubernetes 대시보드를 설치하는 방법

10단계: Metrics Server 설치(Pod 및 노드 리소스 사용량 확인용)

Metrics Server  는 리소스 사용량 데이터의 클러스터 전체 집계 도구입니다.  각 노드에서 Kubelet 에 의해 노출된  요약 API 에서 메트릭을 수집  합니다. 아래 가이드를 사용하여 배포하세요.

11단계: Prometheus/Grafana 모니터링 배포

Prometheus는 Kubernetes 클러스터의 고급 메트릭 기능에 액세스할 수 있는 완전한 솔루션입니다. Grafana는 Prometheus 데이터베이스에 수집 및 저장되는 메트릭의 분석 및 대화형 시각화에 사용됩니다. Kubernetes 클러스터에서 전체 모니터링 스택을 설정하는 방법에 대한 완전한 가이드가 있습니다.

12단계: 영구 저장소 구성 아이디어(선택 사항)

Kubernetes용 영구 스토리지 솔루션도 찾고 있다면 다음을 확인하십시오.

13. Nginx 인그레스 컨트롤러 설치

Nginx가 Kubernetes 워크로드용으로 선호하는 Ingress 컨트롤러인 경우 설치 프로세스에 대한 가이드를 사용할 수 있습니다.

더 많은 가이드:

유사한 Kubernetes 배포 가이드:

Docker Registry V2 Installation

소개

이 글에서는 기본적인 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
위 명령어로 설치된 Registry 기본 설정은 아래 명령어로 확인 할 수 있다.
$ docker exec 860c11dc6921 cat /etc/docker/registry/config.yml
version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3
기본 설정대로 사용하면 제약사항이 너무 많다. 꼭 필요하다고 생각되는 부분만 변경해보도록 하겠다.

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로 연결해 줄 것이기 때문이다.
storage:
  cache:
    blobdescriptor: redis
  filesystem:
    rootdirectory: /var/lib/registry
redis:
  addr: redis:6379

auth

기본 설정대로면 Docker Registry에 접근하기 위해서 그 어떤 인증도 필요하지 않다. Docker Registry V2부터 3rd party 인증시스템을 도입 할 수 있도록 JWT Token Base 인증 서버를 별도로 구현 할 수 있다. 이 부분은 여기서 함께 다루기엔 너무 복잡하므로 상대적으로 간단한 Basic Authorization을 이용하여 인증 시스템을 설정해보도록 하자.
설정을 살펴보면 realm, path를 지정하도록 되어있다. realm은 원하는 값을 넣어주고 path에는 .htpasswd 파일 경로를 넣어준다. docker registry에서 사용 할 .htpasswd 파일은 아래 명령어를 이용해 만들 수 있다. 아이디가 admin, 비밀번호가 1234인 경우이다.
$ docker run --rm --entrypoint htpasswd registry:2.6 -Bbn admin 1234 > .htpasswd
$ cat .htpasswd
admin:$2y$05$WmcysuiS7ZW7jyXMuZS9W.evGsOsnF3yz4o38Xy1KDMLkJbomZEm2

최종적으로 인증 정보가 들어간 설정이 아래와 같이 추가된다.
auth:
  realm: dgoh-registry
  path: /etc/docker/registry/.htpasswd

최종적인 config.yml

version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: redis
  filesystem:
    rootdirectory: /var/lib/registry
redis:
  addr: redis:6379
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
auth:
  htpasswd:
    realm: dgoh-registry
    path: /etc/docker/registry/.htpasswd 
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3

docker-compose.yml 작성

자세히 하려면 봐야 할 설정이 더 있겠지만, 정말 기본적인 설정은 끝났다. 이제 docker-compose.yml을 작성하고 디플로이를 하자.
version: 3
services:
  registry:
    image: registry:2.6
    volumes:
      - /var/lib/registry:/var/lib/registry # host filesystem을 mount
      - ./config.yml:/etc/docker/registry/config.yml:ro # 설정 파일 변경
      - ./.htpasswd:/etc/docker/registry/.htpasswd:ro # htpasswd mount
    links:
      - redis:redis # cache에 사용 할 redis container 연결
    ports:
      - 5000:5000 # 5000번을 이용해 통신
    depends_on:
      - redis # redis가 실행된 후, registry가 실행된다.

  redis:
    image: redis:3.0.7
docker-compose를 이용하여 실행한다.
$ docker-compose up
Starting registry_redis_1 ...
Starting registry_redis_1 ... done
Starting registry_registry_1 ...
Starting registry_registry_1 ... done
Attaching to registry_redis_1, registry_registry_1
마지막으로 registry에 제대로 접속 되는지 docker cli를 통해 확인한다.
$ docker login 127.0.0.1:5000
Username: admin
Password:
Login Succeeded