[번역] CELERY 는 어떻게 파이썬의 GIL 문제를 해결하였는가? Python

이 글은 아래 문서를 해석한 것입니다.
https://viralmoon.blogspot.com/2016/06/how-celery-fixed-pythons-gil-problem.html

해석이 어려운 문장은 원문을 같이 적었으니 참고바랍니다.

====================
====================

최근에 나는 Glyph 의 매우 비 생산적인 포스팅을 다시 읽고 있다. 만약에 당신이 그것을 읽어본적이 없다면 가서 한번 읽어봐라. 내가 여기 요약해줄것네, 오리지날을 읽어보는 것도 꽤 가치가 있을것이다.

지난 10년동안 나는 파이썬의 GIL(Global Interpreter Lock) 의 문제점이 무엇인지 파헤쳐 왔다. GIL 과 관련해 진짜 문제가 되는 포인트는 비동기 I/O 였다.
누구나 알고 있듯이, 쓰레드를 관리는 하는것은 꽤 (정형화 되어) 쉬운편이다. 그냥 요청을 받고 쓰레드를 하나 만들면 마법이 펼처진다. 걱정거리가 있다면 공유자원에 대한 것일 것이다.

하지만 당신은 파이썬으로 그것을 효과적으로 할 수 없다. 왜냐면 쓰레드들이 머신 CPU의 core 수와는 상관없이 인터프리터마다 하나씩밖에 존재하지 않는 GIL 을 두고 경쟁해야 하기 때문이다.

그래서 만일 당신이 인텔 core i7 위에서 작업한다고 해도, 파이썬 쓰레드 가지고는 별 성능 향상을 느끼지는 못할것이다.
실제로는 심지어 더 성능이 나빳고, 이게 바로 파이썬 3.1 버전 이전 GIL 의 문제점이었다. (멀티 코어 환경에서의 쓰레딩은 코드 동작을 더 느리게 만들었고, 실제 동작은 더 구렸다.)


asynchronous I/O 가 문제인가? (Is asynchronous I/O a problem?)

근래의 프로그래밍에서 대부분의 이슈들이 I/O 문제로 수렴된다. 참 클래식한 답변이다.

예를들어, DB에서 데이터를 읽어오는 것은 I/O 작업이다. - 당신은 데이터를 읽어와 fetch 할때까지 system 은 뭔가 다른 작업을 할수 있다. 예를들어 다른 요청을 처리한다든지... 몇 년전, 내가 Twisted project 에 맞닥뜨렸을때, 그것(파이썬의 I/O문제)은 모든 면에서 나를 참 힘들게 했었다. (= Years ago when I encountered the Twisted project that was the reasoning for all the hoops it put you through.)

그 (내가겪은) 문제의 원인이 될수 있는 상황들로 파이썬 asyncio 측에서 말한 내용은 아래와 같다.
(= The reasoning for asyncio's recent addition to Python is)

"싱글 스레드에 coroutine을 활용한 동시성 코드를 작성하는것, 소켓 및 다른 자원들에 대해 multiplexing I/O 접근이 이루어 지는것, 네트워크 클라이언트와 서버를 운영하는것"

여기엔 내가 얘기해주고 싶은 몇가지 전제조건이 있다.
- 싱글스레드 환경에 동시성코드를 작성할 필요가 있어야 함.
- 꽤 자주 multiplex I/O 가 필요해야함
- coroutine 을 사용해야함

무엇보다도, 우리가 정말 싱글 스레드 + 동시성 코드가 필요할까?
지난 10년간 나는 누군가 "이 코드는 concurrent 하지만 single threaded 해야합니다" 라고 콕 찝어 말하는 케이스를 본적이 없다.
내 생각에 여기서 중요한 것은 우리가 동시성을 원하고 그것이 (진정한 동시성을 가질 수는 없는) GIL 과 관련하여 가장 흔한 인자라는 것입니다. (=I think the idea here is really that we want concurrent and that's the most often argument we see with regard to the GIL - that we can't have true concurrency.)

내가 최근에 명백히 깨달은 것은 우리는 정말 동시성이 필요 없다는 것입니다. - 곧 다시 이 주제로 되돌아올텐데, 그 전에 코루틴 녀석을 제껴버리기 위해 두 단어로 된 가정을 좀 하겠습니다.
사람들이 코루틴을 필요로 할까요? 네 하지만 production 코드는 아닙니다. 제가 너무 제 의견을 고집하는것일수도 있지만, 저는 여러 언어로 동시성 코드를 작성했으나 단 한번도 코루틴만큼 가독성이 떨어지는 코드를 본 적이 없습니다
만약에 코드가 읽혀지기를 위해 쓰여진 것이라면, 코루틴은 당신 눈에서 피눈물이 나게 하기 위해 쓰여진 것입니다( 그만큼 알아 보기 힘들다는 뜻인듯)

또한 cooperative concurrency, 즉 코루틴은 꽤 오래전에 버려졋습니다. 왜그럴까요?
왜냐하면 코루틴의 기본적인 전제는 "협력"이기 때문입니다.
선점형 동시성은 공평한 자원의 사용을 강요, 아니 최소 지향합니다.
하지만 코루틴은 그럴수가 없죠 - 만약 당신의 코루틴이 (자원을 점유하고 액세스를) 막으면, 그것은 막히고 당신은 그냥 기다려야 합니다. 그리고 당신의 스레드는 다른 모든 코루틴과 함께 기다려야 합니다.
그것이 실제로 코루틴의 가장 큰 문제입니다.

만약 당신의 문제가 쉘스크립트로 피보나치 숫자계산 코드를 만들기 어려운 것이라면 차라리 그게 낫습니다. 하지만 이 추운곳(지옥같은 실서비스? 같은느낌)에서는 서버가 다운되고, 커넥션 타임아웃이 난무하고, 다 읽지도 못할(잘 알지도 못하는) 오픈소스 라이브러리들을 설치해야만 합니다.

다시 동시성으로 돌아가서 - 제가 사용가능한 스레드 개수에 제약을 받아본적이 있을까요?
아뇨 단한번도... 저는 코루틴도, 동시성 따위도 필요 없다고 생각합니다.
제 생각에 우리에게 필요한것은(우리 노력이 투자되어야 하는곳은) non-blocking 코드입니다.


Blocking code 이슈에 대해 (The issue with blocking code)

우리가 blocking 이라고 여기는 경우는 대부분 다음 둘중 하나이다.
정말 blocked 되었거나, 일이 완료되는데 수년 정도가 걸리는 경우이다.
인간이기때문에 우리는 참을성이 없다. 그런데 우리는 기계가 일을 끝내는데 특히 더 기다리기 힘들어 한다. 하지만 우리가 오랫동안 기다릴 것인지와 block 되는것을 피할 것인지에 대한 선택 역시 같은 문제에 봉착한다.
우리는 빨리 끝날 수 있는 일을 기다리기가 싫다. 뭔가 느린 DB 요청이 끝날때까지 기다리는 동안 다른 사용자에게는 그냥 빠른 응답을 해주고 싶다.
선점형 concurrency (= 스레드)는 매우 이러한 요구사항을 처리하기 좋은 방법이고, 몇가지 주요한 이점들이 있다.
- 코드 가독성이 좋다.
- 공유자원을 효과적으로 사용한다.
스레딩 방식의 가독성에 대해서는 - 딱히 말할것은 없지만, 읽기 쉽진 않지만 확실히 코루틴보다는 낫다.
자원과 관련해서는 - 상세 구현방식에 대해 조금 더 낫다가 아닌 훨씬 낫다고 말할 수 있다. 하지만 대부분은은 2개의 core 를 사용하거나 2 core 머신을 사용한다.
그래서 일반적인 threading 을 위해서 보통 우리는 요렇게 코드를 짠다.

def handle_user_request(request):
    thread_processing_data(request.data).start()
    return Response("OK", request)


그리고 만약 thread_processing_data 에서 타임아웃이 나면, 에러가 날것이다. 그리고 만일 가능하다면 두번째 core를 사용할 것이다. 꽤 괜찮다. 그리고 이것을 정확하게는 아니지만 비슷하게 파이썬으로 짤 수 있다. thread_processing_data 대신에 우리는 processing data 부분을 process로 감싼다.
물론 나는 그 좋은 multiprocessing library 에 대해 말하고 있는 것이다.
하지만 그게 정말로 더 좋을까?
난 여전히 몇가지 개념을 더 이해해야 하고, 그중 특히, 자원이 process 간에 어떻게 공유되는지 에 대해 확실히 알아야 한다.
더 좋은 방법이 있을까?


Lockless 의 승리 그리고 Celery (Lockless for the win and Celery)

내가 프로그래머로서 바라는 한가지가 있다면 block 이 되지않는 code 이다. 나는 그것이 프로세스를 활용하든, 스레드를 활용하든, transactional memory 든 어떤 마법이든 상관없다.

한 작업의 단위를 만들고 우선순위 등의 파라미터를 정하면 당신이 할 일은 끝이다. 그런 일을 할수 있게 해주는 파이썬 패키지가 하나 있으니 그게 바로 celery 다.
Celery는 하나의 프로젝트이며, 당신이 첫번째 Task를 돌리기전에 엄청나게 다양한 조합으로 써먹을수 있다. 그리고 task를 돌리기 시작하면, 정말 굉장하다.
예를들어 , 회사에서 다양한 SNS 데이터를 가져오는 system이 있다고 해보자. 지금처럼, API 콜은 시간이 걸리고, 당신은 네트웤 연결 후 데이터를 보내 리턴을 받아오기 까지의 시간을 표시해야만 한다고 해보자.

For example:

In [8]: %time update_metrics(e)
CPU times: user 1.2 s, sys: 137 ms, total: 1.34 s
Wall time: 1.35 s


음,,, 1초 이상걸리면 사용자향 코드에 사용할 수 없다. 하지만 난 이 코드를 최초 진입 화면에 들어갈 때애만 사용하고자 한다. 어떻게 할 수 있을까?
celery를 사용하면 난 그냥 update_metrics 함수를 task로 감싸 이렇게 만들수 있다.

update_metrics.apply_async((entry.id,), queue=settings.HIGH_PRIORITY_FRONTEND_QUEUE_KEY)

설명을 덧붙이자면:
update_metrics: cost 가 좀 있는 작업으로서, 이제 non-blocking 방식으로 실행된다.
queue: 여러 queue 중 어떤 것이 이 task를 handling 해야할지를 의미하는 parameter

update_metrics 는 처리되는데 장시간이 소요된다. - 하지만 Celery 덕에, 난 그것을 염려할 필요가 없게 되었다:
- 그것은 사용자 액션에의해 정확히 동작한다.
- 코드는 매우 가독성이 높고 직관적이다.
- 자원이 사용가능할때 사용된다.

그리고 무엇보다도 중요한것이: 코드가 I/O 를 하던말던, yield 를 써야 하는지 어쩐지, CPU 혹은 I/O 로 느려진것인지 아닌지 걱정할 필요가 전혀 없다.


Celery 의 능력 (Things Celery can do)

당신의 문제가 100개 URL을 긁어와서 사용자가 지정한 3개 단어의 출현빈도를 측정하는 것이라고 해보자.
일반적으로 이것은 몇가지 문제에 봉착한다 - 긁어온 URL들을 조합해야하고, connection은 timeout 이 날 수 있고, 작업이 끝날때까지 기다려야 하며, 게다가 어쩌면 사용자가 입력한 데이터를 저장해야 할수도 있다.
Celery 없이는 이런문제 해결이 정말 악몽같을 텐데, Celery를 쓰면 난 그냥 아래와 같은 Task 만 작성하면 땡이다:


import requests
from collections import Counter
from celery import task, chord, chain

@task(ignore_result=False)
def find_frequency(link_text, user_words):
    # we live the implementation as an exercise to the reader :)
    return {'foo': 1, 'bar': 1, 'baz': 1}

@task(ignore_result=False)
def scrape_url(url):
    response = requests.get(url)
    if response.status_code == 200:
        return response.text

@task(ignore_result=False)
def aggregate_results(results):
    counter = Counter()
    for result in results:
        counter.update(result)
    # do something with data
    return counter


user_submitted_words = ['foo', 'bar', 'john']
urls_to_scrape = []

async_result = chord(
    [chain(
        scrape_url.subtask(args=(url,)), 
        find_frequency.subtask(args=(user_submitted_words,)))
        for url in urls_to_scrape]
    )(aggregate_results.s())


이 코드예제의 알짜배기만 설명하면 이렇다:
- chain 은 tasks 들의 파이프라인을 만들수 있게 해주어 하나의 결과물이 다른 것으로 전달된다.
- chord 는 여러 task의 그룹을 만들어서 그 task 들이 모두 완료되었을때 특정한 하나의 task를 실행할 수 있도록 해준다.

여기서의 장점은 꽤 크다:
- 당신은 이것이 어떻게 실행될지 관심을 가질 필요가 없다. 그것은 스레드이거나 다중 process 이거나 coroutine 일 것이다.(Celery 어느정도 규모의 모든 종류 pool을 제공한다.)
- since you don't ??? (이라고 쓰여있는데... 갑자기 왜 쓰여있는지 모르겠음)

아아, 하지만 이 케이스에 또 꽤 큰 단점도 하나 있다:
- Celery task가 모두 함수여서, scrape_url.subtask(args=(url,)) 라는 그닥 가독성이 좋진 않은 문법을 써야만 한다.
- Celery는 task들의 import path를 깨끗히 해야하고, 각 task는 함수여야 한다. 주로 tasks.py 파일에 둔다. - 다른 task 안에 define(정의) 또는 submit(제출?) 작업을 할수 없다.
- 우리가 chained task 를 정의 하는것을 다른 어떤 task의 body 에 들어있는 task로 call할 수 없기 때문에, chord 나 chain 과 같은 코드를 복잡하게 하는 것들이 생겼다


잠글수 없는 성질 ? (Lockless?)

위에 나열한 문제 이외에, 나에게 가장 큰 문제는 결이 고운(=부드러운) 컨트롤이 부족하다는 것이다.
Queue는 lockless한 programming 모델을 얻기 위한 매우 원시적인 방법이다.

위 예제는 당신이 배치 작업들을 실행한뒤 이 결과를 모두 종합(aggregate)했다고 볼 수 있다. - 이게 맵리듀스로 짜면 한 30 라인정도 코드가 된다.
하지만 좀 더 어려운 디자인을 생각해보자. - 만일 concurrency 를 지원하지 않는 object 가 있다고 가정해보자. 완전히 lockless 하고 blocking 되지도 않는데 읽기도 해야하며 쓰기도 해야한다고 해보자.
어떻게 해야할까?

먼저 이렇게 할것이다 (난 여기서 당신이 Django integration 을 쓴다고 가정한다)

./manage.py celeryd -Q serial_queue -c 1

이게 하나의 worker 가 최대 한개의 task를 병렬로 돌리도록 하기위한 코드 전부이다


SERIAL_QUEUE_NAME = 'serial_queue'

@task()
def read_value():
    return get_lockless_api_value()

@task()
def increment_value():
    value = get_lockless_api_value()
    new_value = value + 1
    set_lockless_api_value(new_value)

result = read_value.apply_async(queue=SERIAL_QUEUE_NAME)
result = increment_value.apply_async(queue=SERIAL_QUEUE_NAME)
result = read_value.apply_async(queue=SERIAL_QUEUE_NAME)



그렇게 함으로써, 아무 특별한것도 없이 lock도 없이, GIL문제도 없이 우리는 값을 읽고 쓸 수 있게 된다. 물론, 한가지 주요 문제점이 있다. - 우리는 concurrent 한 읽기를 할수 없다 (가능해야 할지라도)


Final remarks (마지막으로 하고싶은 말)

나에게 있어, 이글은 GIL 과 couroutines/asyncio 에 대한 논쟁을 어느정도 요약했다고 본다. 내 생각에 우리가 파이썬 core 에 대해 가지고 있는 주요 문제는 그것이 C의 영향을 매우 많이 받았다는 것이다. 하지만 이것은 파이썬의 약점이다. 그리고 이 노력에 타당한 이유를 찾을 수 없다. (뭔 노력을 말하는건지...;;)

수백개의 회사들이 파이썬 코드를 중요 로직에 사용하고 있다 - 그리고 그것은 동기적인 single-threaded 코드이다. (Django가 얼마나 인기가 많은지 한번 찾아봐라) 어쩌면 몇십, 몇천의 사용자들을 그 회사들이 상대하고 있는데도 말이다.
내 생각에 만약 우리가 정말 제대로 된 concurrency를 파이썬으로 제공하고 싶다면, 이게(celery 말하는듯..) 그 방법이지 않을까 싶다. 프로그래머들이 직접 queue를 정의 하도록 한, 완전한 lockless paradigm에 queue를 사용한 원시적 방법을 소개 한다.

대부분의 것들은 이미 Celery로 구현되었다. 파이썬으로 그것을 사용하려면, 필요한 만큼의 worker와 queue 를 관리할수 있도록 python interpreter 를 확장 시키는것만 남았다. nested tasks 의 정의(definition)와 분배(dispatch)를 가능케 하기위해 약간의 syntax 용이성(=sugar)도 좀 가미하고 말이다.
그럼 꽤 쓸만해질 것이다.

내 생각에 우리는 프로그래머로서 concurrent 한 코드를 전혀 원한적이 없다.
그리고 정말 난 단언컨대, 코루틴이나 multiplex I/O 따위 필요 없었다.

내가 원하는건 내 아이디어를 내가 생각한대로 효과적으로 표현하기 위한 도구이다.
thread 나 concurrency 를 추상화해주면서...
그리고 lockless 한 패러다임이 그것을 해결할수 있도록 길러내(fostering)주면서...


[Django Field 커스터마이징 #1] 템플릿 값 및 코드 활용 값의 사용자화 Python

장고 코딩을 하다보면, 
DB에 저장해야할 데이터와 
form 을 활용하여 템플릿에서 표시할 데이터 형식이 다를때가 있다.

prepare_value() 으로 템플릿에서 사용할 값의 형태를 만들어 리턴하고

to_python() 으로 템플릿에서 코드상으로 최종 활용할 형태를 만들어 리턴하면 됨.


아래 간단한 예제를 첨부한다.

class CommaSeparatedCharField(forms.CharField):
"""
리스트 데이터를 콤마로 구분된 단일 스트링 형태로 변경하여 표시해주고
저장시에는 리스트 형태로 변경해주는 필드

화면표시 (str): 11,22,33
코드에서 (list): [11, 22, 33]
"""

def prepare_value(self, value):
if not value:
return ''
if isinstance(value, str):
return value
return ",".join(value)

def to_python(self, value):
to_python_value = super(CommaSeparatedCharField, self).to_python(value)
return to_python_value.split(",")


Slack 블랙 테마 (Black, Dark Theme) 개발 이것저것

1. 슬랙 앱을 끄고 / Terminate Slack App

2.  /Applications/Slack.app/Contents/Resources/app.asar.unpacked/src/static/ssb-interop.js
     위 파일을 열어서 아래 스크립트 첨부 후 저장
     Open File above, and add script below at the end of the file

document.addEventListener('DOMContentLoaded', function() {
 $.ajax({
   url: 'https://cdn.rawgit.com/laCour/slack-night-mode/master/css/raw/black.css',
   success: function(css) {
     $("<style></style>").appendTo('head').html(css);
   }
 });
});

3. 슬랙을 다시 켜보세요 / Reopen Slack App


oh my zsh 설치하기 개발 이것저것

터미널을 조금 더 편리하게 사용할 수 있게 해주는 tool 인 

zsh 를 사용하는법을 간단히 적어보려한다.


설치하기

일단, 설치는 리눅스 또는 mac 에서만 가능하다.

https://github.com/robbyrussell/oh-my-zsh 에 접속하면 install guide 가 나온다.

주요 설정은 ~/.zshrc 에서 하면됨


<플러그인>

플러그인은 주로 다양한 코드 하이라이팅 을 지원한다.
아래에서 쓸만한 것이 있는지 찾아보면 된다.

https://github.com/robbyrussell/oh-my-zsh/tree/master/plugins

플러그인 변경은 ~/.zshrc 에서 plugins 부분을 변경하면 됨
여러개를 적용할 수 있다.

<테마>

테마는 쉘의 기본 표시 컬러나 스타일등을 결정한다.
값의 변경은 ~/.zshrc 에서 ZSH_THEME 부분을 변경하면 됨

아래 위키에서 쓸만한 테마가 있는지 찾아보도록 하자.
https://github.com/robbyrussell/oh-my-zsh/wiki/themes

+ 나는 주로 agnoster 를 쓰는데 이것을 사용하려면 
여기에서 powerline font 를 다운로드 받아다가 설치하여
내가 사용하는 터미널 app 에서 폰트 설정을 변경해 주어야 함.


[번역] Ansible 2 튜토리얼 #5 (Final) - Facts & Vault 개발 이것저것

https://serversforhackers.com/c/an-ansible2-tutorial
위 링크를 번역한 글입니다.
==================================================

Facts

Playbook 을 실행하였을때 항상 처음에 "gathering facts" 라고 뜨는것을 알아차리셨을 겁니다.

Ansible은 그 어떤 Task 라도 실행하기전에 시스템 환경정보 (그것의 provisioning 정보) 를 모읍니다. (프로비저닝=provisioning : 사용자의 요구에 맞게 시스템 자원을 할당, 배치, 배포해 두었다가 필요 시 시스템을 즉시 사용할 수 있는 상태로 미리 준비해 두는 것을 말함) 이러한것을 Facts 라고 부르며 그것은 꽤 방대한 시스템 정보를 담고있는 array 를 가지고 있다. (cpu core 개수, 네트워크가 ipv4 인지 ipv6, 디스크 마운트 정보, 리눅스 버전 등)

Facts 는 종종 Task 와 Template 설정 에서 유용하게 쓰입니다.
예를들어 Nginx 는 흔히 Cpu core 개수만큼 worker processor 를 띄우죠.
이것을 알기 위해서는 당신은 아마 nginx.conf.j2 파일을 아래와 같이 작성해야 할겁니다:

user www-data;
worker_processes {{ ansible_processor_cores }};
pid /var/run/nginx.pid;

# And other configurations...
Or if you have a server with multiple CPU's, you can use:

user www-data;
worker_processes {{ ansible_processor_cores * ansible_processor_count }};
pid /var/run/nginx.pid;

# And other configurations...



Ansible facts 는 모두 "ansible_" 로 시작합니다. 그리고 variable 이 사용될수 있는 모든곳에서 사용이 가능하죠:
(즉, Variable 파일, Task, Template 들을 말합니다)

어떤 facts 가 사용가능한지 아래 내용을 로컬 머신에서 실행시켜 보세요:

# Run against a local server
# Note that we say to use "localhost" instead of defining a hosts file here!
ansible -m setup --connection=local localhost

# Run against a remote server
ansible -i ./hosts remote -m setup




Vault


우리는 종종 우리의 Ansible template 에 어쩔수 없이 민감정보를 저장해야할때가 있습니다.
이때 Ansible Valut 를 사용하면 좋지요

Vault 는 Yaml file 을 암호화 할수 있게 해줍니다. 일반적으로 Variable 에서 사용되죠. Vault 는 Template 이나 Files 에 들어있는 파일은 안되고요, 오직 yaml 파일만 됩니다.

암호화된 파일을 만들때, 당신에게 password 를 물어볼 수 있습니다. 그 패스워드는 해당 파일을 나중에 수정하거나 Roles 또는 Playbook 에서 참조할때 사용하게 됩니다.

패스워드를 만들어서 어딘가 안전한 곳에 잘 보관하세요

여기에 새로운 Variable file 을 만드는 예제가 있습니다 :

$ ansible-vault create vars/main.yml
Vault Password:


암호를 입력하고나면 기본 에디터(vim 또는 nano)로 파일이 열릴겁니다.

EDITOR 환경변수로 지정된 프로그램을 사용합니다. 보통 vim 이죠.
vim 을 사용하지않으면 환경변수를 바꾸시면 됩니다. 아래처럼요

EDITOR=nano ansible-vault edit vars/main.yml

EDITOR 는 사용자의 profile/bash 설정에서 지정할 수 있습니다.
보통 어떤 리눅스를 이용하느냐에 따라 "~/.profile, ~/.bashrc, ~/.zshrc" 또는 비슷한 파일이 사용되죠.

Ansible Vault 자체는 꽤 직관적입니다. 여기 몇가지 명령어를 보세요:

아래 정도는 해석 하실수 있으리라 믿고 넘어갑니다~ ㅎ

$ ansible-vault -h

Usage: ansible-vault [create|decrypt|edit|encrypt|rekey] \
[--help] [options] file_name

Options:
-h, --help show this help message and exit
For the most part, we'll use ansible-vault create|edit /path/to/file.yml. Here, however, are all of the available commands:

create - Create a new file and encrypt it
decrypt - Create a plaintext file from an encrypted file
edit - Edit an already-existing encrypted file
encrypt - Encrypt an existing plain-text file
rekey - Set a new password on a encrypted file
If you have an existing configuration file to encrypt, use ansible-vault encrypt /path/to/file.yml.



Vault Example: Users

위에서 설명한 Vault 를 이용한 예제를 한번 봅시다.
우리에게 또다른 Role 인 "users" 가 있다고 해봅시다:

cd ~/ansible-example/roles
ansible-galaxy init users


저는 Vault 를 새로운 사용자를 만들거나 그 암호를 입력 할 때 사용합니다. User Role 에서 당신은 사용자 암호와 공용키 등을 사용자의 authorized_keys 파일에 추가하기 위해 Variable file 에 넣어둘 수 있습니다.(SSH 접속을 가능하기 하기위해)

"""
도움말: 공용 SSH key 는 원칙적으로는 공개되어도 상관이 없는 값입니다. - 그 값을 열람하는 사람들이 할수 있는 짓이라고 해봐야, 그들의 서버에 내가 접속할 수 있도록 해주는 정도밖에 없지요. 개별키 없이 공용키 하나로는 시스템 접근을 의도적으로 하지못하게 되어있습니다. 그리고 개별키는 이번 Role 에 포함시키지 않을겁니다.
"""

여기에 variable file 예제가 있습니다. Vault 를 이용해서 생성도 가능하고, 암호화도 가능합니다. 물론 수정은 우리가 알아볼 수 있는 평문으로 할수 있어요!

~/ansible-example/roles/users/variables/main.yml:

admin_password: $6$lpQ1DqjZQ25gq9YW$mHZAmGhFpPVVv0JCYUFaDovu8u5EqvQi.Ih
deploy_password: $6$edOqVumZrYW9$d5zj1Ok/G80DrnckixhkQDpXl0fACDfNx2EHnC
common_public_key: ssh-rsa ALongSSHPublicKeyHere


사용자 암호들도 이미 해쉬값이라는 점에 주목하세요. 이 암호를 user 모듈에서 요구하는데, 이 암호를 만드는 법을 아래 Ansible Documentation 링크에서 확인해볼 수 있습니다.
일단 우분투 에서 하는법을 빠르게 살펴볼까요?

# The whois package makes the mkpasswd
# command available on Ubuntu
$ sudo apt-get install -y whois

# Create a password hash
$ mkpasswd --method=SHA-512
Password:


이렇게 하면 user 모듈에서 사용할 수 있는 해쉬값 암호를 얻을 수 있을겁니다.

"""
도움말:
이 variable 파일 내 암호들은 해쉬값이지만, 이 값들을 담고있는 yml 파일을 저는 암호화 하고 싶습니다.
왜냐면 종종 이 파일들은 해쉬되지 않은 API 토큰이나 SSH 개인키 등도 가지고 있을 수 있거든요...
이런경우에는 암호화가 몹시 중요해지죠.
"""

일단 사용자 암호화 공용키를 Variables file 에 넣고나면 우리는 파일을 암호화 하고이 암호화된 변수들을 Task 에서 활용할수 있게됩니다.

ansible-vault encrypt roles/users/variables/main.yml
> INPUT PASSWORD


이제 우리는 암호화된 variable 들을 이용하여 새로운 사용자를 만드는데 사용할 task 파일을 수정하는데 사용할 수 있습니다.

여기 task 파일이 있습니다. 한번 보시죠.

~/ansible-example/roles/users/tasks/main.yml:

- name: Create Admin User
  user:
    name: admin
    password: '{{ admin_password }}'
    groups: sudo
    append: yes
    shell: /bin/bash

- name: Add Admin Authorized Key
  authorized_key:
    user: admin
    key: '{{ common_public_key }}'
    state: present

- name: Create Deploy User
  user:
    name: deploy
    password: '{{ deploy_password }}'
    groups: www-data
    append: yes
    shell: /bin/bash

- name: Add Deployer Authorized Key
  authorized_key:
    user: deploy
    key: '{{ common_public_key }}'
    state: present

이 Task 들은 user 모듈을 이용하여 새로운 사용자를 만들고 그 암호로 variable 파일에 있던 암호값들을 사용합니다.

그리고 또한 authorized_key 모듈을 사용하여 SSH 공개키를 각 서버에 authorized SSH key 로 등록합니다.

암호화된 변수들은 기존의 일반적인 변수처럼 task 파일에서 사용되었습니다. 하지만 이 Role 을 실행하기 위해서는 Ansible 이 묻는 Vault 암호를 정확히 입력해야만 할 것입니다. 왜냐면 암호화된 Variable 을 복호화 해야하니까요.

이제 Role 을 실행하기 위해 Playbook file 인 server.yml 을 수정해봅시다:

---
# Local connection here, yadda yadda yadda
- hosts: local
connection: local
sudo: yes
roles:
- nginx
- user


이 Playbook 을 실행할때는 우리가 암호화된 파일을 가진 Role 을 돌리는 것이기 떄문에 Ansible 이 Vault 암호를 묻도록 다음과 같이 해줘야 합니다.:

ansible-playbook --ask-vault-pass -i ./hosts server.yml


요약

지금까지 우리가 이제까지 살펴본 것들을 한번 적어보았습니다.


- Ansible 설치
- Ansible inventory 파일 설정
- Ansible ad-hoc 명령어 여러 서버에 동시 실행
- 여러개 Task 를 핸들러를 사용하여 실행하는법을 Playbook 을 만들어 실습
- 이제까지 만든 Task 를 Role 로 한층 더 추상화 하였음 (의존성 설정 포함)
   https://github.com/Servers-for-Hackers/ansible-nginx 참고
- Task 를 register (등록) 하는법을 보았고 이것을 활용하여 다른 task 에게 성공시 실행 등과 같은 의존성이 걸리는 방법을 알아봄
- 더 많은 템플릿, 파일 그리고 변수를 우리 Task 에 활용하는법을 알아봄
- Ansible Facts 를 어떻게 통합하는지 알아봄
-
Ansible Vault 로 보안이 필요한 변수를 안전하게 암호화하는 법을 알아봄

우리는 지금까지 꽤 많은 기초지식을 다뤄보았습니다.
하지만 Ansible 은 더 많은 기능이 있답니다 !!! 한번 찾아보셔요~

------------------------------------

이 다음부터는 별 관련 내용이 아니므로 해석을 생략하겠습니다~
지금까지 Ansible Tutorial 번역을 읽어봐 주셔서 감사합니다 !!


- 끝


1 2 3 4 5 6 7 8 9 10 다음


통계 위젯 (블랙)

2296
459
169784

GoogleAdsenseResponsive

Cluster map