2.7.1.KSB_Dockerize.md 8.11 KB
Newer Older
HooYoungAhn's avatar
HooYoungAhn committed
---
html:
  toc: true
  offline: true
export_on_save:
  html: true
---
# Python 모듈 사용하기: KSB Dockerize
---

`ksblib.dockerize` 라이브러리는 사용자가 프로그래밍한 Python 코드를 Docker 이미지로 자동변환하는 기능을 제공합니다. 또한 REST API 기능을 자동으로 추가하여, 사용자 Python 코드로 입력자료를 보내고 결과를 받을 수 있도록 해줍니다.

## 필요 라이브러리

- Python 3.5+
- Docker 18+
  - 명령창에서 `docker` 명령어가 실행되는지 확인

## 테스트 환경
- Ubuntu 16.04 및 Mac OS X
- Windows 에서는 테스트되지 않음

## 사용방법

### 1. 설치하기

`ksblib.dockerize`를 설치하기 위해, KSB 프레임워크 소스코드 폴더 안의 "ksblib" 폴더로 이동해 아래 명령어를 입력합니다.

```sh
$> python setup.py install
```

KSB 프레임워크가 설치되어있지 않다면 설치매뉴얼 (https://csleoss.etri.re.kr/kor/sub05_01.do)을 참고하여 KSB 프레임워크를 설치합니다.

### 2. base.py 와 main_func 생성하기

사용자 Python 코드들이 아래처럼 "modules" 폴더 안에 들어있다고 가정합니다. <b>참고</b>: 아래 구조는 하나의 예이고, 폴더이름 및 구조는 사용자 임의로 생성할 수 있습니다.

```sh
# Example of user-provided folder structure.
-+ modules
    - __init__.py
    - base.py
    - requirements.txt
    - user_python_file_01.py
    - datafile01.pkl
    - ...
    -+ libraries
        - library01.py
        - ...
```

`ksb.dockerize` 라이브러리를 이용하기위해서는 반드시 두개의 파일이 "modules" 폴더 안에 들어있어야 합니다. 하나는 `__init__.py` 이고 다른 하나는 `base.py` 입니다. `__init.py__` 는 비어있는 Python 파일이어도 상관없습니다 . `base.py``main_func` 이라는 함수를 포함하고 있어야 합니다. 예를 들면 아래와 같습니다.

`ksb.dockerize` 라이브러리는 이 `main_func` 을 불러 수행합니다. `x` 는 REST API를 통해 입력받을 데이터로, <b>문자열</b>형태입니다. `x` 는 단순한 Python 문자열이어도 되고 (예: `'Hello World'`), 혹은 Json 문자열이어도 됩니다 (예: `'{"input": 4}'`). 아래 보이는 예제코드는 입력값 `x`가 Json 문자열이라고 가정하고 작성된 코드입니다.

```python
import json

def main_func(x):
    # In this example, x is Python string containing Json data.
    x = json.loads(x)
    input = x['input']

    # Do some work with the input data.
    added_input = input + input

    # Return results in Python string type.
    return str(added_input)
```

아래 코드는 `x` 가 단순 문자열이라고 가정하고 작성된 코드입니다.

```python
def main_func(x):
    # In this example, x is Python string.
    input = x

    # Do some work with the input data.
    added_input = input + input

    # Return results in Python string type.
    return str(added_input)
```

`x` 를 어떤 형태로 정의하고, 이를 `main_func` 에서 어떻게 사용할지는 <b>전적으로 사용자 자유</b>입니다. 물론, 입력으로 오는 `x`의 형태와 그를 처리하는 방식이 <b>잘 맞아야만</b> 합니다. 또한, `main_func` 은 마지막 줄에 보이는 것처럼, <b>반드시 문자열</b>형태의 결과를 `return` 해야합니다.

아래에서 설명하는 모든 내용들은 `x` 가 Json 문자열이라고 가정하고 작성된 코드를 사용합니다.

### 3. main_func 수행 실험하기

`ksb.dockerize` 를 이용하기 전에 `main_func`  이 성공적으로 수행되는지 <b>반드시</b> 테스트되야 합니다. 이를 위해, "modules"의 상위 폴더에서 Python console을 열고 (e.g. `$> python` 혹은 `$> ipython`), 아래와 같이 입력합니다.

```python
from modules.base import main_func

x = '{"input": 4}'
main_func(x)
```

작성한 `main_func` 이 에러없이 수행되도록 `main_func` 및 사용자 Python 코드들을 수정해야합니다.

<b>참고</b>: 실험 중 발생하는 에러는 주로, 1) 해당 모듈이 없다거나 혹은 2) 해당 파일이 없다는 에러일 것입니다. 두 에러 모두 기본적인 Python 프로그래밍 에러로, Google 검색을 통해 해결할 수 있습니다. 간단하게 예를 들면,
- 해당 모듈이 없는 경우
  `import` 시에 "modules" (즉, 최상위 폴더이름) 로부터 시작하면 대부분 해결됩니다. 즉, `from modules.xxx import xxx` 혹은 `import modules.xxx.xxx` 와 같은 식이 됩니다.

- 해당 파일이 없는 경우
  `os.path.dirname(__file__)` 을 파일경로 앞에 붙여주면 대부분 해결됩니다.


### 4. Docker 이미지 생성하기

위 과정까지 문제없이 끝났다면, 아래 Python 코드와 같이 `ksb.dockerize` 를 실행합니다.

```python
from ksblib.dockerize import Dockerize

drize = Dockerize('docker_image_name', '/path/to/modules',
                  requirements=['numpy:scipy>=1.1.0'],  
                  user_args='any_string_values')
drize.build()
drize.run()
```

위 명령어는 "modules" 폴더에 있는 Python 코드들을 Docker 이미지로 만듭니다. 폴더경로 (`/path/to/modules`) 는 절대경로입니다. 생성되는 이미지의 이름을 변경하려면 `docker_image_name` 를 바꾸면 됩니다. `ksblib.dockerize` 는 8080 포트로 REST API를 제공합니다. 다른 포트로 변경하려면, 마지막 줄을 `drize.run(port=9090)` 처럼 변경하면 됩니다.

사용자 Python 코드에서 사용하는 Python 라이브러리가 있다면 Docker 이미지 생성시에 `requirements` 옵션을 이용해 설치할 수 있습니다. 각각의 라이브러리는 `:` 로 구분하면 되며, 버전 정보도 명시할 수 있습니다.

라이브러리를 설치하는 또 다른 방법은, "modules" 폴더 안에 "requirements.txt" 파일을 생성하는 것입니다. "requirements.txt" 의 내용은 아래처럼 작성하면 됩니다.

```sh
numpy
scipy>=1.1.0
```

`ksb.dockerize` 는 "requirements.txt" 안의 라이브러리를 먼저 설치하고, 그 후에 `requirements` 로 명시된 라이브러리들을 설치합니다. 따라서 동일한 라이브러리가 두 곳에 모두 명시되었다면, `requirements` 에 명시된 라이브러리가 최종설치됩니다.

만일 이미 생성된 Docker 이미지가 있고, 다시 만들고 싶지 않다면 위 예제 코드에서 `drize.build()` 부분을 주석처리하면 됩니다.


#### 사용자 변수사용

만일, 특수한 이유로, 입력값 (예: `x`) 에 정보를 담아보내지 않고 따로 전달하고 싶은 파라미터가 있다면, `user_args` 를 사용하면 됩니다. `user_args` 로 전달된 문자열값은 Docker 이미지 생성시에 "ksb_user_args.pkl" 파일로 저장되며, "modules" 폴더에 위치한 Python 코드에서 읽어올 수 있습니다. 예를 들면:

```python
# Any python script in the "modules" folder.
def read_user_args():
    import os
    import pickle

    # Read the pickle file.
    dir_name = os.path.dirname(__file__)
    user_args = pickle.load(open(os.path.join(dir_name,
                            'ksb_user_args.pkl'), 'rb'))

    return user_args
```

"ksb_user_args.pkl" 은 최상위 폴더 (이 예제에서는 "modules")에 저장됩니다. <b>참고</b>: 이 기능은 실험중인 기능으로, 차후에 다른 형태로 변경될 수 있습니다.


### 5. 생성된 Docker 이미지 실험하기

위 과정에 따라 Docker 이미지가 성공적으로 만들어지고 실행되었다면, 아래처럼 실험해 볼 수 있습니다.

```
$> curl -d '{"input": 4}' http://localhost:8080
```

위 명령어를 실행하면, 아래와 같은 작업이 순차적으로 수행됩니다.

1. `curl` 명령어가 문자열 `'{"input": 4}'` 를 REST API를 통해 Docker image 로 전송
2. `ksb.dockerize` 가 입력값을 `main_func` 에 전달
3. `main_func` 이 문자열 `16` 을 반환

혹은, 아래처럼 Python 명령어를 이용해 테스트해볼 수도 있습니다.

```python
import requests

r = requests.post('http://localhost:8080', data='{"input": 4}')
status_code = r.status_code
response = r.text
```

반환되는 값 `response` 의 형식은 Python 문자열입니다.