Visual Studio 터미널 폰트 설정

|

터미널 폰트 설정

vscode에서 터미널 폰트가 깨져서 나오는 경우가 있습니다. 저 같은 경우는 zsh을 쓰다보니 특정 글자에서 다음과 같이 폰트가 깨져서 출력이 되네요.

image

설정 방법은 Preferences > Settings 메뉴로 진입합니다. 그리고 다음 화면과 같이 터미널의 Font Family 항목을 검색합니다.

image

여기서 맨 위의 칸에 다음 그림과 같이 D2Coding을 입력합니다. 물론 PC에 D2Coding 폰트가 설치되어 있어야 합니다.

image

이제 터미널 폰트가 정상적으로 출력되는지 확인합니다.

image

Docker 실전 예제(Nginx 컨테이너 실행)

|

Docker를 이용해서 Nginx 컨테이너 실행하는 예제

Nginx 이미지 다운로드

$ docker pull nginx:latest
latest: Pulling from library/nginx
8998bd30e6a1: Downloading [=====================>                             ]  13.03MB/30.06MB
6fba654dd4ee: Downloading [======================>
8998bd30e6a1: Pull complete
6fba654dd4ee: Pull complete
661b4150d3a3: Pull complete
13b3c8cc8cde: Pull complete
573040833908: Pull complete
5de8f8b958d2: Pull complete
Digest: sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest

다운로드된 이미지 확인

$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
nginx        latest    2e7e2ec411a6   3 weeks ago   134MB

Nginx 컨테이너 실행

PC의 포트는 5050, 컨테이너 내부의 포트는 80 입니다.

$ docker run --name sample_webserver_1 -d -p 5050:80 nginx:latest
8b0debd502109efc121d28c3de74c1b804580807d9df1e6339afb8b2bfbaf493

컨테이너 실행 확인

$ docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                  NAMES
8b0debd50210   nginx:latest   "/docker-entrypoint.…"   56 seconds ago   Up 56 seconds   0.0.0.0:5050->80/tcp   sample_webserver_1

호스트의 포트 확인

로컬 웹브라우저에서 http://localhost:5050 주소로 접속해서 페이지가 잘 열리는 지 확인합니다. 또는 터미널에서 curl localhost:5050 명령어로 확인할 수 있습니다.

컨테이너에서 실행 중인 프로세스 출력

$ docker top sample_webserver_1
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                2021                1996                0                   11:59               ?                   00:00:00            nginx: master process nginx -g daemon off;
uuidd               2077                2021                0                   11:59               ?                   00:00:00            nginx: worker process
uuidd               2078                2021                0                   11:59               ?                   00:00:00            nginx: worker process
uuidd               2079                2021                0                   11:59               ?                   00:00:00            nginx: worker process
uuidd               2080                2021                0                   11:59               ?                   00:00:00            nginx: worker process

컨테이너 정지

$ docker stop sample_webserver_1
sample_webserver_1

pip3 freeze 명령어(requirements.txt)

|

pip3 freeze 명령어

pip3 freeze 명령어를 이용하면 다음과 같이 현재 사용하고 있는 파이썬 패키지 리스트를 확인할 수 있습니다.

$ pip3 freeze

Deprecated==1.2.13
packaging==21.3
pyparsing==3.0.7
redis==4.1.2
wrapt==1.13.3

패키지 리스트 내보내기

다음 명령어를 이용해서 현재 사용중인 패키지 리스트를 requirements.txt에 저장할 수 있습니다.

pip3 freeze > requirements.txt

패키지 리스트 가져와서 설치하기

pip3 install -r requirements.txt

Git Submodule을 이용한 Python 라이브러리 패키지 관리(Docker 빌드 가능)

|

Git Submodule을 이용한 Python 라이브러리 패키지 관리

Git Submodule을 이용한 프로젝트 추가

git submodule add https://github.com/snowdeer/python_sample_library libs/python_sample_library

Install Package

pip3 install libs/python_sample_library

Dockerfile 생성

FROM python:3.8
WORKDIR /app

# Install regular packages
COPY requirements.txt .
RUN pip install -r requirements.txt

# Install submodule packages
COPY libs/python_sample_library libs/python_sample_library
RUN pip3 install libs/python_sample_library

# copy source code
COPY ./ .

# command to run on container start
CMD [ "python", "./app.py"]

Python Redis RPC 예제

|

Pythong Redis RPC

redis_rpc.py

import json
import uuid
import redis
import logging

logger = logging.getLogger('redis_rpc')


class TimeoutException(Exception):
    pass


class Server(object):
    def __init__(self, name, host='localhost', port=6379):
        self.__redis = redis.Redis(host=host, port=port)
        self.__prefix = 'pyredisrpc:'
        self.__queue = self.__prefix + name
        self.__response_expire_time = 60
        self.__methods = {}
        self.__is_running = False

    def start(self):
        logging.debug('RedisRpcServer::start()')
        self.__redis.flushall()

        self.__is_running = True
        while self.__is_running:
            _, req_data = self.__redis.blpop(self.__queue)
            req_data = req_data.decode()
            req_args = self.__parse_request(req_data)
            if req_args is None:
                continue

            req_id, method, params, timeout_check = req_args
            if timeout_check and self.__is_timeout_expired(req_id):
                continue

            self.__call_method(req_id, method, params)

    def shutdown(self):
        logging.debug('RedisRpcServer::shutdown()')
        self.__is_running = False

    def __parse_request(self, req_data):
        try:
            req = json.loads(req_data)
        except json.JSONDecodeError:
            logger.warning(f'Exception - invalid request(JSON Dcode error)\n{req_data}')
            return

        try:
            req_id = req['id']
            method = req['method']
            params = req['params']
        except KeyError as e:
            key = e.args[0]
            self.__send_response(req_id, None, f'Exception - invalid request key({key})')
            return

        if method not in self.__methods:
            self.__send_response(req_id, None, f'Exception - invalid method({method})')
            return

        if type(params) != list or len(params) != 2 or type(params[0]) != list or type(params[1]) != dict:
            self.__send_response(req_id, None, f'Exception - invalid params({params})')
            return

        timeout_check = req.get('timeout') == 1

        return req_id, method, params, timeout_check

    def __is_timeout_expired(self, req_id):
        timeout_key = self.__prefix + req_id + ':timeout'
        return self.__redis.get(timeout_key) is None

    def __call_method(self, req_id, method, params):
        func = self.__methods[method]
        params_args = params[0]
        params_kw = params[1]

        try:
            result = func(*params_args, **params_kw)
            logger.info(f'{method}() is requested. params: {params}, result: {result}')
            self.__send_response(req_id, result)
        except Exception as e:
            self.__send_response(req_id, None, f'Exception - {repr(e)}')

    def __send_response(self, req_id, result, error_message=None):
        if error_message is not None:
            logger.warning(f'Exception - {error_message}')

        result = {'id': req_id, 'result': result, 'error': error_message}
        
        key = self.__prefix + req_id
        self.__redis.rpush(key, json.dumps(result))
        self.__redis.expire(key, self.__response_expire_time)

    def method(self, f):
        self.__methods[f.__name__] = f


class Client(object):
    def __init__(self, name, host='localhost', port=6379, timeout=0):
        self.__redis = redis.Redis(host=host, port=port)
        self.__prefix = 'pyredisrpc:'
        self.__queue = self.__prefix + name
        self.__timeout = timeout

    def __call(self, method, params):
        req_id = uuid.uuid4().hex
        req = {'id': req_id, 'method': method, 'params': params}

        if self.__timeout != 0:
            req['timeout'] = 1
            timeout_key = self.__prefix + req_id + ':timeout'
            self.__redis.set(timeout_key, 1, self.__timeout)

        self.__redis.rpush(self.__queue, json.dumps(req))
        key = self.__prefix + req_id
        res = self.__redis.blpop(key, self.__timeout)

        if not res:
            raise TimeoutException(req, key)

        _, response_data = res
        response = json.loads(response_data.decode())

        if response['error'] is not None:
            raise Exception(response['error'])

        return response['result']

    def __getattr__(self, method):
        def wrap(*args, **kw):
            return self.__call(method, [args, kw])

        return wrap

server.py

import logging

import redis_rpc as pyredisrpc
from colored_log_handler import ColoredLogHandler

logging.basicConfig(level="DEBUG", handlers=[ColoredLogHandler()])
logger = logging.getLogger('main')

server = pyredisrpc.Server('snowdeer-redis-rpc-example')


@server.method
def add(a, b):
    return a + b


try:
    server.start()
except KeyboardInterrupt:
    print("Interrupted")

server.shutdown()

client.py

import logging
import time
import random

import redis_rpc as pyredisrpc
from colored_log_handler import ColoredLogHandler

logging.basicConfig(level="DEBUG", handlers=[ColoredLogHandler()])

client = pyredisrpc.Client('snowdeer-redis-rpc-example', timeout=1)

for i in range(0, 200):
    x = random.randint(1, 10)
    y = random.randint(1, 10)

    ret = client.add(x, y)

    if ret == (x + y):
        logging.info(f'# test({i}) - ({x}, {y} --> {ret})')
    else:
        logging.warning(f'# test({i}) - ({x}, {y} --> {ret})')

    time.sleep(0.5)