본문 바로가기

클라우드

Cloud #12

하나의 컨테이너에 여러 사용자가 접속하는 것이 가능하기도 하지만 컨테이너는 원래 Linux처럼 다중 사용자용이 아니고 단일 사용자용으로 개발되었기 때문에 로그인 인증이나 사용자 관리가 없어서 다중 접속이 가능하고 sudo su 했을 때 별도의 root 패스워드를 묻지 않는 것이다. 컨테이너는 가상서버가 아니라 프로세스로 실행되는 실행환경에 맞게 고안된 기능이다.

 

호스트란 노드와 노드 안의 파드, 그리고 파드 안의 컨테이너가 실행되게 해주는 프레임 호스트로써 Ubuntu 18.04과 같은 호스트 머신을 말한다. master01, worker01, worker02와 같은 노드나 노드 안의 파드(컨테이너)의 실체는 이 Ubuntu 호스트의 프로세스이다. 컨테이너에서 uname -r을 해보면 호스트의 커널이 보인다. , 컨테이너는 하나의 독립적인 운영체제처럼 보이지만 실은 호스트의 커널을 공유해서 동작하는 리눅스의 프로세스일 뿐이다!!!

 

  컨테이너 이미지

  이미지를 만들 때 바탕이 되는 이미지를 베이스 이미지라고 하는데 리눅스의 공유 라이브러리, 동적 링크, 그리고 로드에 필요한 기초적인 파일들이 있어야 한다. 도커 허브에는 다양한 미들웨어나 프로그래밍 언어가 포함된 이미지가 여럿 등록되어져 있다. 이들 모두는 가볍고 오픈 소스이다.

분류     공식 이미지

리눅스   alpine, busybox, ubuntu, centos, debian, fedora, amazonlinux, opensuse, oraclelinux

언어     node.js, GoLang, php, python, openjdk, ruby, jruby, perl, erlang, pypy, mono, gcc, rails, ibmjava, rust, swift <- java는 컴파일하는 무거운 프로그램이어서 당연히 없다

NoSQL  redis, mongo, memcached, cassandra, couchbase

SQL      postgres, mysql, mariadb, percona <-oracle은 유료이기 때문에 당연히 없다

Web     nginx, httpd

Servlet/JSP tomcat(웹서버), jetty, webshpere-liberty <-Servlet/JSP Java 프로그램을 웹에 맞게 변형해주는 중간 프로그램

콘텐츠 관리 wordpress, ghost, drupal

컨테이너 registry, docker, swam, hello-world

로그와 매트릭 분석 ElasticSearch, InfluxDB, logstash, telegraf, kibana

CI/CD    maven, jenkins, sonarqube

빈 이미지 scatch

 

  Dockerfile에 운영체제와 필요한 패키지들을 기술하고 BUILD 과정으로 이미지를 만들면 (CI)매우 짧은 시간에 컨테이너를 실행시켜서 필요한 내용을 배포(CD)시킬 수 있다. 이 이미지에는 이미 운영체제와 필요한 패키지가 모두 포함되어 있으므로 배포 시 추가적인 시간이 발생하지 않아서 짧게 사용하고 폐기하는 벡터로 잡는 것이 좋다. 만일 어플에 변경 사항이 있다면 Dockerfile에서 그 내용만 변경해서 기술한 뒤 다시 이미지를 만들면 이미 기존의 빌드 시 내용이 캐시되어 있어서 중복되는 부분은 별도로 빌드되지 않고 변경 부분만 빌드하므로 시간도 매우 짧게 걸린다.

 

  컨테이너와 네트워크

  실행 중인 컨테이너는 IP_주소를 할당받기 때문에 컨테이너 간 통신이 가능하다. Ubuntu 호스트 내에서 접근 가능한 전용 네트워크를 통하면 예를 들어서 어플과 데이터베이스를 연결하는 것도 가능하다. 도커 허브에는 Rocket. Chat, ownCloud, Redmine, 그리고 WordPress 등 컨테이너 간의 연결을 이용하는 어플 이미지가 여럿 있다.

 

- 컨테이너 네트워크

  컨테이너 네트워크는 도커 간의 네트워크이다. 도커 허브에 등록된 많은 어플들은 --link를 사용해서 컨테이너를 연동하라고 안내하고 있다. 하지만 docker network를 이용하는 것이 최근의 추세이다. 쿠버네티스에서도 클러스터간 네트워크를 가능하게 해주는 cluster network가 있다.

 

  docker에서 network 명령어를 사용하면 컨테이너 간 네트워크를 생성하거나 삭제할 수 있다. 이들 명령어 몇 가지를 보면

docker network ls : 컨테이너 네트워크를 리스트로 표시

docker network inspect : 네트워크_명을 지정해서 내용 표시

docker network create : 컨테이너 네트워크 생성

docker network rm ; 컨테이너 네트워크 삭제

docker network connect : 컨테이너를 컨테이너 네트워크에 접속

docker network disconnect : 컨테이너를 컨테이너 네트워크에서 분리하는 명령어 등이 있다.

 

  중요한 것은 컨테이너 Nginx 웹 서버가 worker01 노드에서 실행되고 있어도 외부 호스트 Ubuntu에서 이 웹 서버 컨테이너의 접속은 master01 노드가 Control-Plane이므로 master01 노드를 통해서 들어가 한다는 것이다!!!. 만일 worker01에서 Nginx가 실행되다가 죽으면 master Control-Plane이 자동으로 이 Nginx 서비스를 worker02로 이동해서 Nginx 서비스를 지속시켜주어야 하기 때문이다!!!

 

=>지금까지의 실습에서 보면

a. 같은 네트워크에 속해 있는 컨테이너들은 서로 통신이 되지만

b. 다른 네트워크에 있는 컨테이너와는 통신이 되지 않는다. 이들이 통신 되게 하려면 컨테이너 서비스에 외부에서 접근할 수 있어야 하는데 포트-포워드 기법(노드의 8080: 컨테이너의 80)으로 통신하거나

c. 서로 다른 이들 컨테이너 네트워크들을 연결해주는 라우터가 있으면 훨씬 좋을 것이다. 이런 이유로 소프트웨어적으로 라우터 기능을 하게 하는 SDN(Software Defined Network)가 클라우드에서 절대적으로 필요하다!!!!!

 

- 네트워크 삭제

docker network ls 해서 기존에 생성했던 네트워크들을 보고

my-net를 없앤다면, 먼저

docker nertwork disconnect my-net webser10

  docker network disconnect my-net cent10식으로 해서 현재 이 my-net 네트워크에 연결되어져 있는 모든 컨테이너들의 연결을 각각 끊어주고

docker network rm my-net 해주면 된다.

docker network ls 해서 삭제된 것을 확인한다.

=>docker network rm prune 하면 모든 네트워크가 삭제된다.

 

  MySQL 공식 이미지에 환경 변수를 설정하는 컨테이너 API 기법을 사용해서 컨테이너를 만들면 이미지의 재사용성이 매우 좋아질 수 있다. 실행 명령어에서 -e 뒤에 환경 변수는 MYSQL_ROOT_PASSWORD를 외부에서 입력하면서 실행한다는 의미이다. 이렇게 하면 MySQL 이미지에는 root의 패스워드를 지정해서 만들지 않고 실행하면서 외부 환경변수로 패스워드를 지정하기 때문에 이미지에는 root의 패스워드가 저장되지는 않는다.

  이미지로 MySQL을 배포할 때 이미지에 root의 패스워드까지 넣어서 배포할 수는 없을 것이다. 이런 문제를 없애기 위해서는 Dockerfile 설정에서 외부에서 환경변수를 받아들이도록 -e MYSQL_ROOT_PASSWORD=rootoor식으로 주면서 MySQL을 실행시키는 것이 좋다는 것이다. 이렇게 해주면 MySQL에서 root의 패스워드를 rootoor로 인식해서 MySQL이 실행되게 된다. 실행할 때마다 이런 식으로 패스워드를 변경하면서 실행해주면 더욱 완벽한 보안이 된다. 그리고 컨테이너는 어짜피 프로세스이기 때문에 rootoor라는 패스워드를 저장해서 가지고 있지 못한다!!!

 

  컨테이너 API

  컨테이너 API는 파드 내부에서 실행되고 있는 컨테이너들을 외부에서 안 보이게 블랙박스처럼 만들어 주는 인터페이스로써 쿠버네티스 환경에서 사용된다. API에서 인터페이스란 원래 하드웨어 간 접속을 위한 규격이나 케이블 연결을 의미하는 포트와 같은 것으로 이해할 수도 있지만, 소프트웨어에서도 이와 비슷하게 다른 팀이 개발한 프로그램들을 연결하기 위해서 서로 지켜야 할 규격을 인터페이스라고 하기도 한다.

  API는 대상이 되는 파드(APPs 프로그램)를 블랙박스로 취급해서 내부의 활동이나 설정을 외부에서 보지 못하게 하고, 단지 해당 프로그램을 호출해서 사용하게 해준다. 예를 들어서 클라우드 서비스를 특정 Python과 같은 프로그래밍 언어로 호출해서 조작하는 것이 가능해서 도커 컨테이너에 API가 있다면 컨테이너 내부의 해당 API 프로그램 설정 운영을 알지 못해도 사용자는 간단하게 컨테이너의 프로그램을 호출해서 사용할 수 있다. 이를 Transparent(투명하다)라고 부른다.

 

컨테이너 API 종류

  컨테이너 API의 종류부터 알아보고 실습해 보자.

 

- 컨테이너 실행

  컨테이너 내의 어플은 실행 시 환경변수나 실행 인자를 읽어서 그에 맞게 동작되게 할 수 있다.

 

- 헬쓰 체크

  활성 프로브(active probe)는 컨테이너 어플이 초기화가 완료되어 외부로부터 요청을 받을 수 있다는 것을 알리는 인터페이스로써 로드 밸런서가 컨테이너에게 요청을 전달하기 시작해도 되는지 확인할 때 사용된다. 활성 프로브는 어플의 실행 상태가 정상/비정상인지를 알리는 인터페이스이다. 이를 통해서 파드/컨테이너의 비정상이 감지되면 쿠버네티스는 컨테이너를 재가동해서 복구를 시도한다.

 

- 컨테이너 종료

  컨테이너 내의 어플이 종료 요청 시그널(SIGTERM)을 보낼 때를 대비해서 종료 처리를 구현해두는 것이 좋다. 종료 처리는 메모리 값을 보존하거나 데이터베이스의 세션을 종료한 뒤 정상으로 종료하는 것을 말한다. 강제 종료 시그널(SIGKILL)은 제한 시간 내에 종료하지 않을 때 강제로 종료를 요청하는 시그널이다.

 

- 서비스(Service)

  컨테이너에서 돌아가는 어플은 특정 포트를 통해서 외부 클라이언트로부터 요청을 받아들인다. 이를 위해서 컨테이너 포트를 호스트의 IP_주소에 포트 포워딩해서(8080:80식으로) 외부에서의 요청이 들어오게 해야 한다. 쿠버네티스에서는 컨테이너를 담고 있는 파드에 포트를 열어서 외부 클라이언트로부터 요청을 받게 한다.

 

- 로그(Logs)

  많은 수의 컨테이너를 실행하면 로그도 커지므로 도커나 쿠버네티스는 로그를 일괄적으로 관리한다. 컨테이너의 어플은 로그를 파일에 쓰지 않고 표준 출력이나 표준 오류에 쓰는 방식도 가지고 있다.

 

- 후크(Hook)

  쿠버네티스에서는 컨테이너가 실행되거나 종료될 때 컨테이너에서 특정 처리를 실행시킬 수 있는데 파드의 매니페스트에 이런 내용을 기술하면 된다. 컨테이너에서는 후크에 의해 실행될 스크립트 혹은 HTTP 요청 처리 등을 구현할 수 있다.

 

- 퍼시스트 볼륨(Persist Volume)

  컨테이너가 삭제되면 해당 데이터도 삭제되므로 컨테이너는 일시적인 존재이어서 보관이 필요한 데이터는 컨테이너에 저장할 수도 없고 저장도 안 된다. 컨테이너에서 퍼시스트 볼륨을 사용하면 설정 파일을 외부에서 컨테이너/파드에 넣어주는 방법과 컨테이너에서 발생된 데이터를 임시 보관하는 방법이 있을 수 있는데 두 경우 모두 외부 호스트의 특정 디렉터리를 컨테이너의 파일 시스템에 마운트 해서 사용하게 하면 된다. 

  어플이 읽어 들일 파일을 컨테이너 외부에서 넣어주는 방법은 설정 파일을 변경하기 위해서 이미지를 다시 빌드하지 않아도 되므로 컨테이너 재 사용율이 좋아진다. 예를 들어 Nginx의 경우 설정 파일을 담은 호스트 디렉터리를 컨테이너 경로 /etc/nginx/conf.d에 마운트하는 식이다. 또 인증서와 같이 보안에 민감한 파일은 이미지에 담아서 리포지터리에 등록해서는 안 되므로 이 경우도 퍼시스트 볼륨을 사용해서 외부에서 컨테이너에 정보를 넣어주는 것이 좋다. 쿠버네티스에서는 비밀번호와 같이 민감한 데이터를 다루기 위한 시크릿(Secret)과 일반적인 설정 파일을 다루기 위한 컨피그맵(ConfigMap)이 있는데 이들을 이 퍼시스트 볼륨에 넣어두면 된다.

  컨테이너 내에서 환경변수를 사용하면 이미지를 다시 빌드하지 않고 재활용할 수 있다.

 

- 종료 상태

  컨테이너를 종료하려면 PID 1인 프로세스의 Exit 코드를 지정하면 된다. 쿠버네티스에서는 컨테이너가 종료코드 0으로 종료하면 정상으로 보고, 그 외의 값에서는 비정상 종료로 본다.

 

  kubectl run 명령어를 입력해서 결과가 나오는 과정을 보면

kubectl이 쿠버네티스에게 명령을 전달하고 -> local 노드에 이미지가 없으면 원격 리포지터리(hub.docker.com)에서 이미지를 다운받아 오고 -> 노드의 containerd가 컨테이너에 이미지를 얹어서 실행시키고 ->kubectl이 터미널에 실행 결과 메시지를 표시하는 과정인 것을 알 수 있다.

 

  kubectl run 옵션에서

1) --restart=Always를 지정하면 파드를 디플로이먼트 컨트롤러의 제어 아래에서 실행되게 되고,

2) --restart=Never를 지정하면 파드만 독립적으로 실행된다. 파드가 정지되었을 때 재실행 여부에 따라서 옵션을 주면 된다.

3) --restart= 를 생략하면 디폴트로 Always가 실행된다. , 기본적으로 파드는 Deployment에 의해 실행된다는 의미이다. 이때에는 백그라운드로 실행되기 때문에 -it 옵션은 무시된다.  

4) --replicas=숫자를 생략하면 기본 값 1로 설정된다. AVAILABLE 0으로 나오면 파드가 실행되지 못한 것을 의미한다.

=>여기서 run 실행 시 —rm 옵션을 주면 컨테이너를 실행한 뒤 컨테이너가 종료되면 자동으로 이 컨테이너가 삭제되게 된다. 하지만  

kubectl run hello-world --image=hello-world -it --restart=Never --rm 해주면 생성하면서 삭제되므로 의미는 없다.

'클라우드' 카테고리의 다른 글

Cloud #16  (0) 2023.05.12
Cloud #15  (0) 2023.05.12
Cloud #14  (0) 2023.05.12
Cloud #13  (2) 2023.05.12
Cloud #11  (1) 2023.05.12