가상화와 가상머신

가상화

가상화는 단일한 물리 하드웨어 시스템에서 여러 시뮬레이션 환경이나 전용 리소스를 생성할 수 있는 기술

하이퍼바이저라고 불리는 소프트웨어가 하드웨어에 직접 연결되며, 1개의 시스템을 가상머신VM이라는 별도의 고유하고 독립된환경으로 분할할 수 있음

하이퍼바이저라는 소프트웨어가 물리 리소스를 필요로 하는 가상 환경으로부터 물리 리소스를 분리합니다.

하이퍼바이저가 설치된 물리 하드웨어를 호스트라고 하며, 그 리소스를 사용하는 여러 VM을 게스트라고함

APP과 종속성을 분리해 체계화 할 때, 기존의 VMware, VirtualBox 와 같은 OS가상화 방식에서 벗어나, CPU의 가상화기술 (HVM) 을 이용한 KVM (Kernel-based Virtual Machine) 과 반가상화 (Paravitualization) 방식의 Xen이 등장함. 게스트 OS가 필요하긴 하지만, 전체 OS를 가상화하는 방식이 아니었기에 성능이 향상.

리눅스에서는 이러한 방식을 개선하기위해 프로세스를 격리하는 리눅스컨테이너 방식이 등장

도커는 리눅스컨테이너에 여러 기능을 추가하여, APP을 컨테이너로서 좀더 쉽게 사용할 수 있도록 만들어진 오픈소스 프로젝트.

기존의 VMWare, Virtual_Machine가상 머신은 운영체제 인스턴스 하나를 구획화(Compartmentalizing)가 필요한 애플리케이션 각각에 할당하는 방식으로 '분리(Isolation)'를 제공한다. 거의 완전하게 분리시킬 수 있는 방법이지만, 상당한 비용이 수반된다. 각 운영 인스턴스가 앱 자체에 더 효과적으로 할당할 수 있는 메모리와 프로세싱 파워를 소비하기 때문이다.

리눅스 컨테이너를 이해하기 위한 출발점은 cgroups(control groups) 및 네임스페이스(namespaces)이다. 컨테이너와 호스트에서 실행되는 다른 프로세스 사이에 벽을 만드는 리눅스 커널 기능들이다. IBM이 최초 개발한 리눅스 네임스페이스는 시스템 리소스들을 묶어, 프로세스에 전용 할당하는 방식으로 제공한다.

구글이 최초 개발한 리눅스 cgroups는 분리와 CPU, 메모리 등 프로세스 그룹의 시스템 리소스 사용량을 관리한다. 예를 들어, 과학용 컴퓨팅 애플리케이션 같이 많은 CPU 사이클과 메모리를 소비하는 애플리케이션을 운영하는 경우, 애플리케이션을 cgroup에 집어넣어 CPU와 메모리 사용량을 제한할 수 있다.

네임스페이스는 프로세스 하나의 리소스 분리를 처리하고, cgroups는 프로세스 그룹의 리소스를 관리한다. cgroups와 네임스페이스가 결합되면서 리눅스 컨테이너(Linux Containers, LXC)라는 컨테이너 기술이 탄생했다.

가상화종류

호스트 가상화

PC , Server에 OS를 설치하고, 그 위에 가상머신을 설치하여, Host OS 위에서 Guest OS 를 운영하는 방식
각 가상환경마다 가상머신을 생성하고, Guest OS를 설치하게됨
예시로 VirtualBox, VMWare, Parallels 등이 있습니다.
장점으로는 가상의 하드웨어를 운영하는것으로, 호스트운영체제에 큰 제약사항이 없습니다
단점으로는 OS위에 OS가 얹히는 방식이라, 오버헤드가 큽니다.

하이퍼바이저 가상화

Host OS 없이 하드웨어에 하이퍼바이저를 설치하여 사용하는 방식으로
Xen, MS Hyper-V, KVM등이 있고 아마존의 EC2가 Xen을 기반으로 구축되어 있습니다.
호스트OS없이 하드웨어를 직접 제어하기에 효율적으로 리소스를 사용할 수 있습니다.
하지만, 자체적으로 머신에 대한 관리기능이 없기에 관리를 위한 컴퓨터나 콘솔이 필요합니다.
하이퍼바이저에는 [전,반]가상화방식이 있는데, 전가상화는 하드웨어를 완전히 가상화하는방식으로,

전가상화 방식은 하이퍼바이저에서 어떤 OS든 각 OS들의 명령어를 번역해주고, 각 OS들에 자원까지 할당하는 역할까지 하기에 Guest OS 운영체제의 별다른 수정이 필요없지만, 약간 성능이 떨어집니다.

반가상화방식은 전가상화의 성능저하를 막기위해 Hyper Call이라는 인터페이스로 각 OS에 각기다른 번역기를 통해 작동합니다. 전가상화에 비해 성능이 좋지만, 각 Guest OS의 커널을 수정해줘야한다는 단점이있습니다.

컨테이너 가상화

호스트OS위에 컨테이너 관리 소프트웨어를 설치하여, 논리적으로 컨테이너를 나누어 사용
다른 가상화방식들보다 오버헤드가 적어 가볍고 빠르다는 장점이 있습니다.
호스트 운영체제의 커널을 공유하면서 격리된 컴퓨팅 자원을 제공하는 가상화기술입니다
하드웨어를 가상화하는 가상머신과 달리 커널을 공유하는 방식이기에, 실행속도가 빠르고 성능상의 손실이 거의 없습니다.
컨테이너로 실행된 프로세스는 커널을 공유하지만, 리눅스 namespaces, cgroup, chroot등의 커널기능을 활용해 격리되어 실행됩니다.
이러한 격리기술 덕분에 호스트머신에게는 프로세스로 인식되지만, 컨테이너 관점에서는 마치 독립적인 가상머신처럼 보이게 됩니다

CGROUPS

Control Group의 약자로 다수의 프로세스가 포함되어있는 프로세스그룹 단위로 자원사용(CPU,I/O,Memory,Network 등)을 재한하고 격리시키는 리눅스 커널 기능

namespace

프로세스를 실행할 때 시스템의 리소스를 분리해서 실행할 수 있도록 도와주는 기능

chroot

가상 루트 디렉터리
보안 등을 위해 새로운 가상의 루트디렉토리를 생성하여 원격 서비스로 접속할 경우 이 디렉토리의 상위로 이동이 불가능하게 하는 명령어
파일을 격리시켜 독립공간을 생성

리눅스 컨테이너 장점

  • 운영체제 수준의 가상화: 컨테이너는 운영체제 수준의 가상화 기술입니다. 별도의 하드웨어 에뮬레이션 없이 리눅스 커널을 공유해 컨테이너를 실행하며, 게스트OS 관리가 필요하지 않습니다.
  • 빠른 속도와 효율성: 하드웨어 에뮬레이션이 없기 때문에 컨테이너는 아주 빠르게 실행됩니다. 프로세스 격리를 위해 아주 약간의 오버헤드가 있지만 일반적인 프로세스를 실행하는 것과 거의 차이가 없습니다. 또한 하나의 머신에서 프로세스만큼 많이 실행하는 것이 가능합니다.
  • 높은 이식성portability: 모든 컨테이너는 호스트의 환경이 아닌 독자적인 실행 환경을 가지고 있습니다. 이 환경은 파일들로 구성되며, 이미지 형식으로 공유될 수 있습니다. 리눅스 커널을 사용하고 같은 컨테이너 런타임을 사용할 경우 컨테이너의 실행 환경을 공유하고 손쉽게 재현할 수 있습니다.
  • 상태를 가지지 않음stateless: 컨테이너가 실행되는 환경은 독립적이기 때문에, 다른 컨테이너에게 영향을 주지 않습니다. 도커와 같이 이미지 기반으로 컨테이너를 실행하는 경우 특정 실행 환경을 쉽게 재사용할 수 있습니다.
  •  

유니온 파일 시스템 Union File System

UFS에서는 기존 레이어(하위 레이어) 위에 새로운 레이어(상위 레이어)가 쌓일 경우, 하위 레이어는 읽기 전용 상태가 된다. 또한, 상위 레이어에서 하위 레이어에 쓰기 작업을 수행할 경우, 하위 레이어를 복사하여 사용(CoW)하기 때문에 상위 레이어에서는 하위 레이어에 아무런 영향을 주지 않는다.

도커에서는 레이어가 크게 컨테이너 레이어(상위)와 이미지 레이어(하위)로 구분된다. UFS 특성에 따라서 컨테이너가 파일 시스템에 쓰기 작업을 수행할 경우, 실질적으로는 하위 레이어의 복사본에 해당 작업을 수행하기 때문에 서로 다른 컨테이너가 하위 레이어를 공유하고 있어도 서로 독립적인 파일 시스템 운용이 가능해진다.



https://loopstudy.tistory.com/20
https://loopstudy.tistory.com/22?category=1004771
https://velog.io/@koo8624/Docker-유니온-파일-시스템-Union-File-System
https://www.redhat.com/ko/topics/containers/what-is-docker

'Python > Docker' 카테고리의 다른 글

docker-compose  (0) 2021.03.10
Docker  (0) 2021.03.09

참조 : python decorator (데코레이터) 어렵지 않아요 (tistory.com) , Python 데코레이터(Decorator). 데코레이터란? | by 홍찬기 | Medium

데코레이터에 대하여 위의 블로그에서 이미 잘 설명해놓았다.

데코레이터를 활용해서 간단히 팩토리얼을 메모이제이션을 통해 구하는 함수를 만들어보았다.

그런데,, 뭐가 제일 Pythonic한지, 데코레이터와 메모이제이션을 잘 활용했다고 해야할지 모르겠어서.. 혹시나 이 글을 보게되는 여러분들께 어떤게 더 파이썬스러운 코드인지 의견을 여쭙고싶다.

1번코드

def memoize_factorial(f): 
    memory = {1:1,2:2} 
    def wrapper(*args, **kargs):
        k=f(*args,**kargs)
        i=2
        while i<k:
            i+=1
            memory[i] = memory[i-1] * i
        return memory[k]
    return wrapper

@memoize_factorial
def facto(num): 
    return num
facto(5)

2번코드

def memoize_factorial(func): 
    memory = {1:1,2:2}
    def wrapper(*args, **kargs):
        k=args[0]
        if k in memory : return memory[k]
        else:
            i = len(memory)
            while i<k:
                memory[i+1] = memory[i]*(i+1)
                i+=1
        return memory[k]
    return wrapper

@memoize_factorial
def facto(num):
    if num==1: return 1
    else: return num * facto(num-1)
facto(3) , facto(5)

3번코드

def memoize_factorial(f): 
    memory = {} 
    def wrap(args):
        if args in memory:
            return memory[args]
        memory[args]=f(args)
        return memory[args]
    return wrap

@memoize_factorial
def facto(num): 
    if num==1:
        return 1
    else :
        return num * facto(num-1)    
facto(5)

'Python' 카테고리의 다른 글

lambda , filter, map  (0) 2021.05.11
Python Generator 제너레이터, yield  (0) 2021.05.11
Global에 대하여  (0) 2021.05.11
Object-Oriented Programming  (0) 2021.05.11

lambda

간편하게, 한줄로 함수를 만들어서 쉽게 쓸 수 있다.

아래는 일부러 2개의 인자를 받도록 lambda 함수를 만들고, 인자를 1개만 줘보았다.

positional argument 'y' 가 missing 되었다고 TypeError를 띄워준다

func = lambda x,y : x+y
func(10)

func = lambda x,y : x+y
list(map(func, [1,2],[2,3] ) )
# [3,5]
list(map(func, (1,2),(2,3)))
# [3,5]

2개의 인자를 받는 lambda 식에서 map으로 인자를 주려면 어찌줘야할까.. 해봤다

zip도 안먹히고, 하나로 묶으니 안먹힌다

아무래도 map함수자체가 iterable한 녀석한테 적용해주려다보니 이렇게 되는것같다.

a=[150,130, 80,60, 40,30, 20,15, 5,5]
list(map(lambda x: 
        'A' if x > 100
        else 'B' if x>50
        else 'C' if x>25
        else 'D' if x>10
        else 'E', a))

# ['A', 'A', 'B', 'B', 'C', 'C', 'D', 'D', 'E', 'E']

lambda 안에서 굳이 이런식으로 if, else를 계속 쓸 수 있다.

filter

반복 가능한 객체에서 특정 조건에 맞는 요소만 가져온다.

filter에 지정한 함수의 반환값이 True일 때에만 해당 요소를 갖고오는것

a=[150,130, 80,60, 40,30, 20,15, 5,5]
list(filter(lambda x : x%30==0 , a))
# [150, 60, 30]

30으로 나누어 떨어지는수 (30의 배수)인 녀석들만 출력하도록 하였다

reduce

반복가능한 객체의 각 요소를 지정된 함수로 처리한 뒤 이전 결과와 누적하여 반환

from functools import reduce
a=[150,130, 80,60, 40,30, 20,15, 5,5]
reduce(lambda x,y : print(x,y) , a)
'''
150 130
None 80
None 60
None 40
None 30
None 20
None 15
None 5
None 5
'''

reduce(lambda x,y : x+y , [1,2,3,4,5,6,7,8,9])
# 45

맨 처음 람다의 인자로 index 0, 1이 주어진 후 그 뒤로는 y에만 전달된다.

그런데 굳이.. list의 컴프리헨션이 가능한 상황에서... 이걸 쓸 필요가 있을까 싶긴하다.

'Python' 카테고리의 다른 글

Python Decorator에 대하여 @ 데코레이터  (0) 2021.06.14
Python Generator 제너레이터, yield  (0) 2021.05.11
Global에 대하여  (0) 2021.05.11
Object-Oriented Programming  (0) 2021.05.11

참조 : https://wikidocs.net/16069

iterator를 생성해 주는 함수이며 함수 안에 yield 키워드를 사용

iterable한 순서가 지정됨

순서의 다음값이 필요에 따라 계산됨

함수 내부의 로컬 변수를 통해 내부 상태가 유지

무한한 순서의 객체를 모델링 할 수 있음

문득 든 생각이... 피보나치수열 만들때 이걸 쓰면 되는거 아닐까? 라는 생각이 들었고 ... 생각보다 잘 되는데 싶다

def fibo(a,b):
    fibolist=[a,b]
    while True:
        now = fibolist[-1] + fibolist[-2]
        fibolist.append(now)
        yield now

fibos = fibo(1,1)
fibolist=[1,1]
for i in range(10):
    fibolist.append(next(fibos))
fibolist
# [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]

이렇게 yield방식을 생각했을때 제일먼저 떠오른게 피보나치였고, 이게 과연 피보나치의 근본접근법이라고 여겨지는 Dynamic programming에서 Memoization과 얼마나 속도 차이가 날까? 테스트해보았다

# Yield를 사용했을 때
%%time
def fibo(a,b):
    fibolist=[a,b]
    while True:
        now = fibolist[-1] + fibolist[-2]
        fibolist.append(now)
        yield now

def getfibo(n):    
    fibos = fibo(1,1)
    fibolist=[1,1]
    for i in range(n-2):
        fibolist.append(next(fibos))
    return fibolist
tmp=getfibo(100*1000)
len(tmp)
# Memoization
%%time
def getfibo(n):
    fibos=[1,1]
    while len(fibos)<n :
        fibos.append(fibos[-1]+fibos[-2])
    return fibos
tmp = getfibo(100*1000)
len(tmp)

둘의 시간차이가 거의 없다...

'Python' 카테고리의 다른 글

Python Decorator에 대하여 @ 데코레이터  (0) 2021.06.14
lambda , filter, map  (0) 2021.05.11
Global에 대하여  (0) 2021.05.11
Object-Oriented Programming  (0) 2021.05.11

참조 : https://dojang.io/mod/page/view.php?id=2364

전역변수 지역변수

함수를 포함하여 스크립트 전체에서 접근할 수 있는 변수를 전역변수 (global variable)라고 부른다

g_val = 'g_val'
def v1():
    global gloval
    gloval = 'gloval'
    loval = 'local-val'
    print()
    print(g_val)
    print(gloval)
    print(loval)

v1()
# g_val
# gloval
# local-val

g_val은 전역변수로 선언되어있는 상태에서 v1이 실행, v1에서 gloval이라는 전역변수가 선언되고,

loval이라는 지역변수가 선언되었음

g_val = 'g_val'
def v1():
    global gloval
    gloval = 'gloval'
    loval = 'local-val'
    print()
    print(g_val)
    print(gloval)
    print(loval)

def v2(g_val, gloval):
    loval = 'v2 loval'
    print()
    print(g_val, gloval)
    print(loval)

v1()
v2(g_val, gloval)
'''
g_val
gloval
local-val

g_val gloval
'''

v1을 실행한 후, v2를 실행하였다.

g_val에 'g_val'이 담긴 상태에서, v1을 통해 gloval이 전역변수로 선언되었으며, 그렇기에 함수밖에서 v2로 gloval을 파라미터로 넘겨줄 수 있었다.

def argv2(**args):
    args['gloval'] = 'ddd'
    print()
    print(args)
    print(args['gloval'])
    print(gloval)
'''
{'gloval': 'ddd', 'dfdf': 'fdfd'}
ddd
gloval
'''

args로 받게되면, 딕셔너리로 담기는데, 이 때 여기서 gloval을 변경했을때는 어떨까? 고쳐보았다.

그 결과 args로 담겨진 gloval은 변경해도, 전역변수의 변경이 되지 않는것을 볼 수 있었다.

결국 마지막 print(gloval) 로, gloval을 확인해본 결과, 바뀌지않았다.

그냥 dictionary에서 Key = gloval 의 값이 바뀌어버린것으로만 보면 될것같다.

def v3(g_val, gloval):
    g_val = '333 changed'
    gloval = '33 changed groval'
    print()
    print(g_val, gloval)

v1()

v3(g_val, gloval)    ## 함수에서 전역변수를 받아서 변경해봄
print()
print(g_val, gloval)

'''
g_val
gloval
local-val

333 changed 33 changed groval

g_val, gloval
'''

v3에서 파라미터로 받은 변수를 변경한다면 어떻게될지 테스트해보았다.

g_val과, gloval을 둘 다 변경시켜서 v3내에서 출력한 결과 바뀌어서 출력된다.

그러나 외부에서 다시 g_val, gloval을 출력해본결과 바뀌지 않았다.

def v4():
    global g_val
    print(g_val)
    g_val = 'v4_gval'

v4()
print(g_val)
'''
g_val
v4_val
'''

이번엔 v4함수를 만들어서 , 그 안에서 global로 g_val을 불러오고나서 변경시켜보았다

잘 변경된다!

이로써 배우게 된 점 ....

전역변수를 설정하는것에 있어서 조심조심히 사용해야할것이며, 그것을 어떠한 함수 내에서 사용하면서 변경할 경우, 왠만하면 직접참조하지않는것이 좋을것같다. 정...변경이 필요하다면 return을 받아서 그걸 밖에서 저장하는 방식이 일관성있게 더 낫지않을까..싶다.
정말 부득이하게 어떠한 함수 내에서 전역변수를 변경하거나, 선언할 일이 생긴다면 global 키워드를 사용해야한다!

'Python' 카테고리의 다른 글

Python Decorator에 대하여 @ 데코레이터  (0) 2021.06.14
lambda , filter, map  (0) 2021.05.11
Python Generator 제너레이터, yield  (0) 2021.05.11
Object-Oriented Programming  (0) 2021.05.11

일단 OOP라는 개념에서 객체(Object)를 먼저 알아야한다.
객체란 어떤 속성(Attributes), 값(Value), 함수(Methods)를 갖는 데이터를 말한다.

캡슐화

내부 속성(변수), 함수를 하나로 묶어서 클래스로 선언하는 일반적인 개념
캡슐화 하지 않으면 특정 기능(함수, 변수)에 직접 접근하는 상황이 되고, 기능이 많아질수록 재사용성이 떨어진다.

이전에 JAVA를 배울때 접근제어에 대해 살짝 배운적이 있는데, Python 에서도 어느정도 접근제어가 가능하다!

name      # Public
_name     # Protected
__name    # Private
__asd__   # Public

어디서든 호출 가능한 Public과 동일 클래스나 인스턴스에서만 접근 가능한 Protected,
직접접근을 차단하는 Private를 선언할 수 있다.
그리고, 접미사(postfix)에 언더바가 2개 (__) 이상인 경우에 public으로 간주한다.!!
하지만... 이름만 복잡해질뿐, 기존의 객체지향언어에서 사용하던것만큼 엄격한 수준으로 접근관리가 이루어지지는 않는다..

상속과 포함

상속 Inheritance → 상위 클래스의 모든 기능(함수,변수)를 재사용 할 수 있음
포함 Composition → 다른 클래스의 일부 기능(함수)만을 재사용함

class Person:
    def __init__(self,age):
        self.age = age

    def printPerson(self):
        print(f'Person_printPerson , Age : {self.age}')
        return self.age

class Student:
    def __init__(self, age):
        self.age = age
        # Student가 Person에 포함되기 위해, Person 클래스에 대한 object 생성
        self.p = Person(self.age+100)       

    def personComposit(self,age):
        # 포함개념적용된 Student 클래스의 Person object의 함수
        return age, self.p.printPerson()  

# object 생성
s = Student(10) # 한 번 생성된 object는 파라미터가 변하지 않는 이상 출력값 또한 변하지 않는다.
p = Person(20)

s.p.printPerson()
s.personComposit(40)

위와같은 경우 Student의 p라는 속성으로 Person인스턴스를 하나 갖는다.

따라서 s.p.printPerson()s.personComposit(40) 의 결과가 같다

추상화

Object의 기능에 따라 추상클래스(상위)를 상송받아 개별적으로 하위클래스를 생성

실제 실행되는 기능은 선언된 추상 클래스를 상속받은 다른 클래스의 메소드에서 확인가능
→ @abstractmethod를 선언해서 사용해줘야함

프로그램이 복잡해 지는 경우 1차적인 설계를 위해 기능을 추상화 시켜놓고 활용여부는 차후 결정하기 위함

from abc import *
class People(metaclass=ABCMeta):
    @abstractmethod
    def character(self):
        pass
class Student(People):
    def character(self, pow, think):
        self.pow=pow
        self.think=think
        print(self.pow, self.think)

# p=People()
s=Student()
s.character('pow','think')

위의 People 클래스는 추상클래스로, 그 안에 def character 라는 추상메소드를 갖고있다.

그렇기에 Student에서 People을 상속받으며 character 의 상세 내용을 구체화해줘야 한다.

class Student_2(People):
    def __init__(self):
        super().__init__()

s2 = Student_2()
# TypeError: Can't instantiate abstract class Student_2 with abstract methods character

People 클래스를 상속받은 Student_2에서 character를 구체화하지않는다면 위와같은 타입에러를 볼 수 있다.

다형성

구션되는 하위클래스에 따라 클래스를 다르게 처리하는 기능 → 상위클래스의 기능을 변경하여 사용하느것 (그대로 사용하지 않음)

class A:
    def run(self):
        print('A run')

    def play(self):
        print("A play")

class B(A):
    def run(self):
        print("B run")
    def play(self):
        print("B play")

class C(A):
    def run(self):
        print("C run")

a=A()
b=B()
c=C()
c.play()
## A play

A ← B , C 의 형태로 상속한다

C에는 play() 메소드가 없는데, 이를 실행할경우 A.play() 를 실행한것과 같은 효과를 받는다

class C(B):
    def run(self):
        print("C run")
c=C()
c.play()
## B play

위의 상황에서 A ← B ← C로 상속관계가 이뤄진다.

이때 c.play()를 하게될 경우 B의 play() 가 실행된다.

참조 : https://velog.io/@seonj102/OOPS-파이썬

'Python' 카테고리의 다른 글

Python Decorator에 대하여 @ 데코레이터  (0) 2021.06.14
lambda , filter, map  (0) 2021.05.11
Python Generator 제너레이터, yield  (0) 2021.05.11
Global에 대하여  (0) 2021.05.11

단순히 하나의 python 파일만 실행하는것이 아닌 여러개의 파일과 그 안의 코드들이 자신들의 역할에 맞게 작동하면서 Application이 작동한다.
이렇게 작동한다는 것 자체가, 서로서로 import를 해주며 엮인다는 뜻이며, 이중 자칫하면 Circular Import문제로 App의 실행이 중단될 수 있다.

Circular Import란?

두개 이상의 python파일이 서로를 import하면서, 서로 불러오는것이 반복되어 결국 무한루프에 빠지게 되는것.

# appdirectory.__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

db = SQLAlchemy()
migrate = Migrate()

def create_app(config=None):
    app = Flask(__name__)

    db.init_app(app)
    migrate.init_app(app, db)

    from kokoa.routes import main_route
    app.register_blueprint(main_route.bp)

    return app

if __name__ == "__main__":
    app = create_app()
    app.run(debug=True)

위와같이 create_app을 통해, app을 실행시키도록하며, flask실행보다 먼저 db와 migrate를 생성하므로, 향후 다른곳에서 appdirectory에서 db 또는 app을 참조할 때 순환임포트 문제가 발생하지 않도록 해준다!

Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. To learn more about all the features of Compose, see the list of features.

여러 도커 Application을 정의하고 실행하는 툴로, YAML파일을 통해 각각의 서비스를 설정할수있다.

version: "3.9"  # optional since v1.27.0
services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - .:/code
      - logvolume01:/var/log
    links:
      - redis
  redis:
    image: redis
volumes:
  logvolume01: {}

(공식문서 Docker-compose 예시)

도커 엔진 1.13.1 버전 이상, 도커컴포즈 버전 1.6.0 이상에서 사용할 수 있는 기능으로,
컨테이너 실행에 필요한 옵션을 docker-compose.yml 이라는 파일에 담아서 일괄적으로 실행시켜준다

version

첫 줄에는 version 을 명시해준다.

version : '3'

3으로 시작하는 최신버전을 사용한다는 뜻

버전 3부터는 links: 를 통하여 연결해줄 필요가 없다

services

실행하려는 컨테이너들을 정의한다.
서비스의 이름을 지정해주고, 그 안에 이제 도커이미지명, 환경변수 등등 다양한 것들을 입력해 넣어서 구성해준다.

서비스 이름 (지정)

사용하려는 컨테이너를 지정하는 이름과같다

services: #서비스부터 어떤 컨테이너를 이용할 것인지 작성
  postgresql: #db이라는 서비스를 이용할것인데 

postgre 서비스 이름을 postgresql로 지정하였다

image

사용하려는 이미지

services: #서비스부터 어떤 컨테이너를 이용할 것인지 작성
  postgresql: #db이라는 서비스를 이용할것인데 
    image: postgres:12 #그 서비스의 이미지는 이것과 같고

postgresql에서 사용할 도커 이미지를 적는다.

postgres (docker.com) 설명 사진 (hub.docker.com)

volumes

volumes:
      - ./servers.json:/pgadmin4/servers.json

docker run 으로 db컨테이너 실행시 --volume 옵션으로 연결하던 로컨의 데이터와 같다.
위 과정을 통해 프로젝트 루트 아래의 pgadmin4 에 servers.json 을 저장하였다

environment

services:
  postgresql:
    environment:
      POSTGRES_DB : Chinook
      POSTGRES_USER: postgres_admin
      POSTGRES_PASSWORD: password

Chinook이라는 Database로, User와 비밀번호를 지정하였다.

restart

restart 공식문서
재실행 여부를 설정한다.

Restarts all stopped and running services.
If you make changes to your docker-compose.yml configuration these changes are not reflected after running this command.
For example, changes to environment variables (which are added after a container is built, but before the container’s command is executed) are not updated after restarting.
If you are looking to configure a service’s restart policy, please refer to restart in Compose file v3 and restart in Compose v2. Note that if you are deploying a stack in swarm mode, you should use restart_policy, instead.

어떠한 구성을 변경했을 때 변경사항의 즉각적인 반영 여부를 나타내는 것 같다.


services: #서비스부터 어떤 컨테이너를 이용할 것인지 작성
  postgresql: #db이라는 서비스를 이용할것인데 
    restart: unless-stopped

윗 부분에서 이제 Postgre를 설정했으면, 이제 Admin부분을 만들어준다

  pgadmin:
    image: dpage/pgadmin4:4
    container_name : pgadmin
    ports:
      - 5050:80 #외부에서 5050으로 들어왔을 때 내부에서 80번을 보여주겠다
    environment:
      # PGADMIN_DEFAULT_HOST: postgres_admin
      PGADMIN_DEFAULT_EMAIL: user1@pgadmin.com
      PGADMIN_DEFAULT_PASSWORD: secret
    depends_on:
      - postgresql
    volumes:
      - ./servers.json:/pgadmin4/servers.json
    restart: unless-stopped  

image는 pgadmin4의 4번태그 이미지를 활용하며, 서비스이름은 pgadmin, container_name또한 동일하게 설정.
ports부분이 있는데, 이 부분을 이제 들어온곳과 연결할 곳을 입력해줘야한다. 여기서는 5050번 포트로 들어왔을때, 내부의 80번 포트와 연결한다는 것이다.
그리고 이후 pgadmin페이지 (localhost:5050)에서 로그인할 이메일,비밀번호를 설정하였다.
depends_on 부분이 있다.
이 부분은 앞서 작성한 postgresql이 실행되고 나서, pgadmin이 실행된다는 뜻이다


이제 실제로 compose만들고, up해서 연결해보자

version: '3.8'
services:
  db:
    container_name: pg_container # pgadmin에서 서버연결할 때 연결할 
    image: postgres
    restart: always
    environment:
      POSTGRES_DB: test_db
      POSTGRES_USER: root
      POSTGRES_PASSWORD: root
    ports:
      - "5432:5432"
  pgadmin:
    container_name: pgadmin4_container
    image: dpage/pgadmin4
    restart: always
    environment:
      PGADMIN_DEFAULT_EMAIL: admin@admin.com
      PGADMIN_DEFAULT_PASSWORD: root
    ports:
      - "5050:80"
    depends_on: 
      - db

docker-composet up --force-recreate 명령어를 통해 up을 하면서, 혹시 이전에 저장했다가 테스트용으로 했던게 있어도, 새로 반영하도록한다. 

이제 pgadmin4 사이트에서

yml파일에 적어놓은, DEFAULT_EMAIL , DEFAULT_PASSWORD를 통하여 로그인한다 

로그인을 하면  browser페이지로 넘어가는데, 이곳에서 서버를 클릭해도 아직 연결되어있지않다, server - create

서버생성(연결) connection에 기존에 yml파일에서 세팅했던것들을 입력하여 연결!

서버연결에 성공하고나면, 내가 yml에서 만들었던 db의 POSTGRES_DB가 생성되어있다.

 

이후, db와 pgadmin을 띄우고난 뒤 DBeaver를 통해 연결해보자 

정말 간단하다! 그냥 연결하면서 POSTGRES 설정해주고, localhost의 5432포트와, 
Database이름은 내가 기존에 생성한 db로 하고, user와 password를 입력하면 연결완료!

참조 :
도커(Docker) 컴포즈를 활용하여 완벽한 개발 환경 구성하기 | 44BITS
docker-compose공식문서

'Python > Docker' 카테고리의 다른 글

[Docker] 도커를 알기위해 알아야하는 것들  (0) 2021.08.31
Docker  (0) 2021.03.09

드디어 만났다 도커!

너무도 많은 엔지니어들이 애용하고있는 도커다!

도커는 어플리케이션의 실행환경을 코드로 작성할 수 있고, OS와 격리시켜 관리할 수 있는 환경을 제공한다.
이게 얼마나 강력한것이냐?!!
Application들은 운영체제(Linux, Windows, MacOS 등) 에 따라, 각각의 환경을 구분하여 개발해야 한다. 왜냐면 각각 os마다 파일시스템, 텍스트의 문법 등 사소한것부터 큰것까지 무지하게 다르기 때문에, 이것들을 하나하나 다 맞춰주면서 개발을 해줘야 했다는 뜻이다. 얼마나 귀찮았을까..
그리고, 그렇게 개발할 때 환경변수도 맞춰줘야하고, 사용자권한, 폴더들, 방화벽, 포트 등등 수많은 설정이 있기에, 이것들을 다 하나하나 관리해줘야했다는 것을 의미하며 굉장히 번거로운 작업일수밖에 없었다.

도커 이전에는 가상머신을 이용했다고한다. 하나의 컴퓨터에 다른 컴퓨터를 통째로 만드는것이다.
맥에서 인터넷뱅킹을 위하여 페러렐즈를 통해 윈도우를 사용하던것과 같다.
그런데, 가상머신이 생각보다 성능 손실이 상당하다는 것이 큰 단점이었다.

리눅스 컨테이너

위의 가상머신의 성능문제를 해결하기 위해 생겨난 기술이 리눅스컨테이너이다.
컨테이너 안에 가상 공간을 만들어, 실행파일을 호스트 컴퓨터에서 직접 실행하는 것이다.
리눅스의 cgroup, namespaces가 제공하는 기술이다.
필요한 라이브러리와, 어플리케이션을 모다, 하나의 서버처럼 구성한 것으로, 컨테이너 내의 네트워크 설정, 환경변수 등 시스템 자원이 독립적으로 구성된다.

  1. 프로세스의 구획화
  • 특정 컨테이너에서 작동하는 프로세스는 기본적으로 그 컨테이너 안에서만 엑세스 가능
  • 컨테이너 안에서 실행되는 프로세스는 다른 컨테이너의 프로세스에 영향을 줄 수 없음
  1. 네트워크의 구획화
  • 하나의 컨테이너에 하나의 IP가 할당
  1. 파일 시스템의 구획화
  • 컨테이너 안에서 사용되는 파일시스템은 구획화되어있음
  • 해당 컨테이너의 명령이나 파일 등의 엑세스를 제한할 수 있음

왼쪽 : 도커                                 오른쪽 : 가상머신

도커

리눅스 컨테이너 기술을 활용, App 실행환경을 코드로 작성할 수 있고, OS와 격리화 하여 관리하는 기술.
환경표준화, 수작업으로 일치시키는 환경 구성,


도커 컨테이너를 만들 때, 환경변수를 넣어주는 부분에서 자꾸 에러가 났다. 여러 방법을 찾아보니, txt파일로 직접 만들어서
docker container run 이후 인자로 --env-file = [filename]를 통하여, txt파일 내의 환경변수를 자동 매핑시켜 실행할 수 있었다. 

vi env_list.txt
#---
S3S1N2_Name=장형준
github_username=jun1116 
#---입력 후 Esc, :wq 로 종료
##### Vi 에디터를 통하여 env_list.txt 에 환경변수를 작성
 
docker container run -it --name first --env-file=env_list.txt aibcontents/n312:1.0 bash
# --실행, 후 ls로 내부 파일 확인, python 명령어를 통해 해당 파이썬 파일 실행 

docker inspect를 통해 확인해보니, 내가 입력한 환경변수가 성공적으로 등록되었다

'Python > Docker' 카테고리의 다른 글

[Docker] 도커를 알기위해 알아야하는 것들  (0) 2021.08.31
docker-compose  (0) 2021.03.10

+ Recent posts