Android, Cocos2d, Java Web, Linux, reading...

git 기본 명령어 사용하기


이번 포스팅에서는 기본적인 git 명령어에 대해 다뤄보려고 한다. 그 전에 먼저 git을 설치해야 하는데

https://git-scm.com/downloads로 들어가서 각 환경에 맞는 버전을 설치하면 된다. 필자의 경우 Window 버전용을 설치했다. 설치 과정은 검색하면 많이 나오니 설치했다고 가정하고 Github 계정도 만들어져 있다고 가정하고 시작하겠다.

먼저 Github의 repository(원격 저장소)에 올릴 파일을 저장할 로컬 저장소로 간다. 필자의 경우 바탕화면의 pooh_git/POOH 폴더를 지정했다.

여기서 마우스 오른쪽 클릭을 하면 ‘Git Bash here’이 있다. 이를 클릭하면

현재 경로와 함께 CLI(Command Line Interface) 창이 나온다. git –version 명령어를 통해 설치된 git의 버전도 확인할 수 있다.

그럼 이제 본격적으로 로컬 저장소에서 Github(원격 저장소)로 파일을 업로드해보자.

가장 먼저 해야할 것은 Github 사이트에 등록된 username과 e-mail 주소를 입력하는 것이다. 이는 git commit 시마다 사용해야 하는 정보이기 때문에 초기에 설정해놓아야 한다. 그렇다고 매번 할 필요는 없고 처음 설정할 때 한번만 해주면 된다.

잘 설정되었는지 확인하려면 git config –list로 user.name과 user.email 부분을 보면 된다.

이제 현재 로컬 저장소(~/Desktop/pooh_git/POOH)를 git 로컬 저장소로 등록할 차례이다.

$ git init

로컬 저장소를 git 로컬 저장소로 초기화해주는 명령어다. 저장소(repository)나 디렉토리 안에서 이 명령을 실행하기 전까지는 그냥 일반 폴더일 뿐이다. git init을 먼저 입력해야 추가적인 git 명령어들을 적용할 수 있다. 그 다음 줄에 (master)가 나왔는데 이는 git init이 잘 적용됐다는 뜻이다.

$ echo “hello, pooh!!” > pooh.txt ~ $ ls

리눅스에서 ‘hello, pooh!!’라는 내용이 담긴 pooh.txt 파일을 만드는 과정과 같다. ls로 파일이 잘 만들어졌는지 확인한다.

$ git status

git 저장소 상태를 체크하는 명령어다. 어떤 파일이 저장소(repository) 안에 있는지, commit이 필요한 변경사항이 있는지, 현재 저장소의 어떤 branch에서 작업하고 있는지 등을 볼 수 있다. 밑을 보면 pooh.txt가 Untracked files라고 나와 있는데, 이는 추적되지 않은 파일, 즉, git에서 아직 관리할 대상이 아니라는 의미이다. 이를 tracked 상태로 바꾸어 git이 관리하는 대상에 포함되도록 해줘야 한다. 그래서 그 밑을 보면 use “git add” to track, 즉, track상태로 바꾸기 위해 “git add” 명령어를 사용하라고 나와있다.

$ git add [파일]

이 명령어는 commit 하기 전에 index에 등록해주는(인덱싱해주는) 명령어이다. 쉽게 말해, commit 하기 전 준비 과정이며 git이 해당 파일들을 지켜보게 해주는 거라고 생각하면 된다. 정확히는 git의 저장소 “스냅샷”에 포함시키는 것이다. [파일] 부분에 .을 입력하면($ git add . ) 현재 폴더의 모든 파일을 인덱싱한다는 의미이다.

(참고 : 밑을 보면 “LF will be replaced by CRLF”라는 경고창이 떠 있다. 이는 Unix 운영체제에서는 파일의 끝을 LF로 대체하고 Windows 운영체제에서는 CRLF로 대체한다는 의미이다. 즉, 단순한 경고창일 뿐 잘 처리가 된 것이다.)

$ git commit -m “pooh.txt commit”

git add로 git이 해당 파일들을 지켜보게 했다면 이제는 git commit으로 git이 버전관리를 하도록 해줘야 한다. 그리고 어떤 변경사항(새 파일 업로드, 수정 등)이 있을 때 이 명령어로 변경사항을 저장한다. 좀 더 자세히 말하면, git add 명령어를 통해 저장소 “스냅샷”에 포함시켰다면 이제는 git commit으로 저장소의 “스냅샷”을 찍는 것이라고 생각하면 된다. 하지만 그렇다고 원격 저장소인 Github에 반영되는 것은 아니다. 현재는 로컬 저장소에만 저장되는 상태이다. 보통은 “git commit -m “[Message]” 형식으로 사용하는데, “[Message]” 부분은 이따가 어떻게 보여지는지 뒤에 나온다.

이제 remote 저장소(원격 저장소)를 만들 차례이다. 위에서 말했지만 필자는 Github에 원격 저장소를 두려고 한다. 그럼 Github에다가 원격 저장소를 만들자.

Repository를 클릭한다.

New 버튼을 눌러 원격(remote) 저장소를 만든다.

필자는 원격 저장소와 로컬 저장소의 이름을 같게 했다. 다르게 해도 전혀 상관없다. 또한 모두가 볼 수 있게 Public으로 했다. 그 밑의 “Initialize this repository with a README”는 체크하면 저장소가 생성됨과 동시에 README 파일이 생기게 되고 체크하지 않으면 그냥 빈 저장소가 생긴다. 필자는 일단 체크하지 않았다. 왜냐하면 다음 포스팅에서 체크할 것이기 때문이다. 일단 지금은 그냥 넘어가자. 그 다음 Create repository 버튼을 누르면

다음과 같은 화면이 나온다. POOH라는 이름의 저장소가 생성된 것인데 중간에 나와있는 주소가 원격 저장소 URL이다(또는 위의 URL에 .git을 붙여주면 된다). 이 원격 저장소도 로컬 저장소처럼 git이 관리할 수 있도록 git에게 원격 저장소가 어디인지 알려줘야 한다. 이 때 사용하는 것이 git remote add 명령어이다.

$ git remote add [단축이름] [원격 저장소 URL]

git이 관리할 수 있도록 git에게 원격 저장소의 위치를 알려주는 명령어로 [단축이름]은 원격 저장소의 닉네임이라고 생각하면 된다. [원격 저장소 URL] 부분에는 URL만 넣으면 안되고 마지막에 .git을 붙여줘야 한다. 아까처럼 주소에 .git이 나와있다면 그대로 입력하면 되지만 URL창에 나와있는 주소로 git remote add하려는 경우는 반드시 끝 부분에 .git을 붙여줘야 한다.

$ git remote

등록된 원격 저장소를 확인하는 명령어이다. -v 옵션을 주면 로컬 저장소가 알고 있는 원격 저장소(POOH)에 대한 모든 항목을 보여준다. 2개가 리스트 된 것은 정보를 원격 저장소에 push하고 fetch 할 수 있다는 것을 뜻한다.

$ git push -u [원격 저장소 닉네임] [브랜치]

로컬 저장소에서 작업한 다음 변경 사항을 원격 저장소에 적용해주는 명령어다. 필자가 입력한 명령어를 해석해보면 원격저장소에 master라는 branch를 생성하고 변경 사항을 push한다는 뜻이다. -u 옵션은 생략해도 되는데, -u 옵션을 사용할 경우 이후에는 git push만으로 전체 입력 효과가 발생한다고 한다(즉, 옵션을 기억함). [원격 저장소 닉네임]은 아까 git remote add의 [단축이름]과 같다.

push가 잘 적용됐는지 원격 저장소로 가보면

pooh.txt가 원격 저장소에 업로드 된 것을 확인할 수 있다. 아까 git commit -m “pooh.txt commit”의 ‘pooh.txt commit’이 바로 해당 파일의 설명 부분인 것을 알 수 있다.




참고 사이트 :

  • http://slowalk.tistory.com/2470
  • http://emflant.tistory.com/123
  • http://freeprog.tistory.com/72
  • https://nolboo.kim/blog/2013/10/06/github-for-beginner/
  • http://paphopu.tistory.com/13

阅读全文 »


vi(m) 편집기로 여러 줄 삭제 및 커서 이동


여태까지 리눅스에서 vi(m) 편집기로 파일을 다룰 때 대부분 짧은 내용들의 파일들만 다뤄서 내용을 수정할 일이 있으면 그냥 delete 버튼을 눌러서 지웠다가 다시 쓰고 그랬는데 코드를 짜다보니까 자연스레 길이가 길어지게 되었다. 이러다 보니 여러 줄을 삭제해야 되는 경우가 생겼고 delete 버튼으로 하나하나 지우기에는 너무 오랜 시간이 걸렸다. 그래서 공부도 하는 겸 간단하게 포스팅해보려고 한다. 여러 줄 삭제하는 방법 외에 간단한 명령어들도 몇 가지 다뤄보겠다.

현재 kkk.txt에는 이런 내용들이 담겨 있다.


먼저 커서 이동 명령어에 대해서 알아보자.

  • Home : 한 줄에서 맨 앞으로 이동하는 명령어
  • End : 한 줄에서 맨 끝으로 이동하는 명령어
  • gg : 첫 행으로 이동하는 명령어
  • G 또는 Shift + g : 마지막 행으로 이동하는 명령어
  • ‘숫자’ + Shift + g 또는 ‘숫자’ + gg: 입력한 숫자에 해당하는 행으로 이동하는 명령어

눈으로 보기 편하게 :set number로 줄번호들이 나오도록 했다. 그 다음 필자는 9번째 줄로 이동하고 싶어서 ‘9+gg’을 입력했다. 그랬더니 그림처럼 9번째 줄로 잘 이동했음을 확인할 수 있다.

코드를 짜다보면 코드가 끊기지 않고(줄바꿈 되지 않고) 길어지는 경우가 있는데 이 경우 n줄이 되더라도 줄번호는 하나로 인식된다. 그래서 그 줄의 코드 중간으로 가려면 한칸 한칸씩 이동해야 하는데 너무 귀찮다. 이런 경우는 특정 단어를 검색하면 빠르다. 방법은

  • /’단어’

이렇게 코드가 길어지면 육안상 6줄인데도 불구하고 줄번호는 이 전체가 155번째 줄이다. 이런 경우는 6줄 전체를 코드 1줄로 인식하기 때문에 아래 방향키가 먹히지도 않는다. 그래서 같은 줄이라고 인식되는 파란색으로 표시된 곳을 수정하고 싶은 경우 오른쪽 방향키로 커서를 하나하나 움직여야 하는데 너무 귀찮다. 이 때 검색을 이용하면 검색된 단어가 있는 곳으로 커서가 이동되기 때문에 바로 수정해 줄 수 있다. 현재 커서 위치는 155번째 줄의 맨 첫 번째에 위치해 있는데 이 상태에서 초록색 부분을 검색하면(다음 그림 맨 밑 - /4,300원)

커서(연두색)가 검색한 단어 있는 곳으로 옮겨짐과 동시에 검색한 부분이 노란색으로 표시되었음을 확인할 수 있다. i(INSERT)를 눌러 바로 수정해주면 된다.


이번엔 여러 줄을 한 번에 삭제하는 방법에 대해 알아보자. 방법은 간단하다.

  • ‘숫자’ + dd

이 명령어는 해당 커서가 위치한 곳에서부터 ‘숫자’에 해당하는 줄의 수만큼을 삭제한다. 숫자를 입력하지 않고 dd만 입력하면 해당 커서가 위치한 1개의 줄만 지운다. 다음 그림을 보자.

여기도 이해하기 쉽게 :set number 명령어를 이용하였다. 필자는 5번 ~ 10번(총 6개) 줄에 해당하는 내용을 지우고 싶다. 그러면 5번으로 커서를 이동시킨 후 “6 + dd” 입력해주면 된다. 5번 ~ 10번을 삭제하면 그 다음 줄, 즉, 11번인 container가 5번자리로 와야 한다. “6 + dd”를 입력해보면

맨 밑에 6 fewer lines 라는 메시지가 뜨면서 6줄이 지워졌음을 확인할 수 있다. 예상대로 5번째 줄에는 container 단어가 위치하게 된다.

그럼 만약 해당 커서가 위치한 곳에서부터 그 아래 내용들을 모두 지우려면 어떻게 해야할까?? 이렇게 하면 된다.

  • dG

커서를 linux 단어에다가 놓고 dG를 입력하면

맨 밑에 14 fewer lines라는 메시지와 함께 linux라는 단어를 포함해서 그 밑의 내용들이 모두 지워졌다.


이번엔 화면 단위 이동 명령어에 대해 알아보자.

  • Page Down, Ctrl + f : 한 화면 밑으로 이동하는 명령어

한 화면 밑으로 이동하게 되면 이전 화면의 맨 밑에서 두 번째 줄이 첫 줄로 시작된다.

또한 이는 화면 크기에 따라 달라질 수 있다. 예를 들어 화면을 최대화해서 20번째 줄까지 보였다면 다음 화면으로 넘어갔을 때는 18번째 줄이 첫 줄로 시작된다. 하지만 화면을 작게 해서 10번째 줄까지 보였다면 다음 화면으로 넘어갈 때는 8번째 줄이 첫 줄로 시작된다는 뜻이다.

  • Page Up, Ctrl + b : 한 화면 위로 이동하는 명령어

위의 Page Down과 Ctrl + f와 반대라고 생각하면 된다. 처음 화면에서 맨 첫 줄이 한 화면 위로 가게 되면 맨 마지막에서 2번째 줄이 된다. 이 역시 화면 크기에 따라 달라질 수 있다.

  • Ctrl + d : 반쪽 화면 밑으로 이동하는 명령어

반쪽 화면 줄이 첫 줄로 이동된다.

이 역시 화면 크기에 따라 달라질 수 있다.

  • Ctrl + u : 반쪽 화면 위로 이동하는 명령어

위의 Ctrl + d와 반대라고 생각하면 된다. 처음 화면에서 맨 첫 줄이 다음 화면의 중간 줄로 가게 된다. 이 역시 화면 크기에 따라 달라질 수 있다.

지금까지 배운 것들을 이용하면 코드가 길어져도 수정해야 될 부분들을 빠르게 수정할 수 있을 것이다.




참고 사이트 :

  • http://www.leafcats.com/115
  • http://blog.syszone.co.kr/196
  • https://tip.daum.net/openknow/3701363

,

阅读全文 »


IndentationError: unindent does not match any outer indentation level


Django에서 파이썬 코드를 돌리던 도중에 이런 에러가 났다.

views.py 81번째 줄에서 에러가 나서 urls.py에도 에러가 발생한 상태이다. 에러가 발생한 views.py로 가보자.

파란색 부분이 에러가 발생한 위치이다. 하지만 코드상 아무리 봐도 잘못된 부분이 없는 것 같았다. 그래서 에러 IndentationError: unindent does not match any outer indentation level을 검색해보니 들여쓰기가 잘못돼서 나는 에러라고 한다. 리눅스의 vim으로 보니까 어느 부분에서 들여쓰기가 잘못됐는지 잘 안 보여서 Pycharm으로 코드를 보기로 했다.

보니까 에러난 곳에 들여쓰기가 한 칸 더 되어있었다. 툴이 좋긴 좋은 것 같다. 위(파란색 표시)에도 빨간색 부분으로 잘못돼있다고 표시가 되어있었다. 들여쓰기를 제대로 맞춰준 후 다시 돌려보면

오류가 뜨지 않고 잘 된다.




참고 사이트 :

  • https://thebook.io/006855/xa/02_10/

,

阅读全文 »


[Django 챗봇] 3-1. 웹 크롤링(1)


저번 포스팅에서 버튼까지 완성을 시켰고 이제 각 버튼에 대한 응답을 처리해야 하는데, 이 챗봇은 ‘학식 메뉴’를 알려주는 챗봇이기 때문에 학식 메뉴에 대한 정보를 가져와서 사용자에게 응답해줘야 한다. 필자는 학식 메뉴를 홈페이지에서 크롤링하여 가져오려고 한다.

  • 웹 크롤링

    : 크롤링(crawling) 혹은 스크래이핑(scraping)이라고도 하며, 웹 페이지를 그대로 가져와서 원하는 데이터를 추출해내는 행위를 말한다. 크롤링하는 소프트웨어는 크롤러(crawler)라고 하며, 웹 크롤러(web crawler)는 world wide web(www)을 탐색하는 컴퓨터 프로그램이다.

현재는 views.py에 keyboard함수와 message함수만 구현되어 있는 상태이다. views.py에 라이브러리 추가한 다음 함수 만들어서 구현해도 되지만 필자는 따로 parsing.py 파일을 만들어서 진행하려고 한다. 크롤링 할 때는 BeautifulSoup 라이브러리를 이용할 것이다. BeautifulSoup은 html을 파싱하는데 사용하는 Python 라이브러리이다.

그 전에 먼저 http request 라이브러리인 requests를 설치해줘야 한다. requests 라이브러리는 파이썬에 내장된 urllib 모듈을 편하게 사용하도록 만든 것으로 설치는 pip를 이용해서 할 수 있다.

$ sudo pip install requests

requests도 정말 좋은 라이브러리지만, 필자는 BeautifulSoup을 사용하려고 한다. 그 이유는 BeautifulSoup가 Python이 이해하는 객체구조로 변환해주기 때문이다. 무슨 말이냐면 위의 req.text는 HTML 소스를 가져오는 부분인데 이 부분은 Python의 문자열(str) 객체를 반환할 뿐이기 때문에 원하는 정보를 추출하기가 어렵다. 좀 더 쉽게 말하면, Python 입장에서는 단지 ‘hihello안녕’ 같은 문자열이라고밖에 인식하지 못한다는 뜻이다. 그래서 이를 BeautifulSoup 라이브러리를 이용해 html 코드를 Python이 이해하는 객체 구조로 변환한 다음 Python으로 원하는 정보를 추출하려고 하는 것이다. BeautifulSoup 역시 pip를 통해 설치하면 된다.

$ sudo pip install bs4

이제 parsing.py를 만들자.

$ vim parsing.py

경로는 지정해주고 싶은대로 해주면 되지만 필자의 경우 pooh_app이 있는 즉, views.py와 같은 경로에다가 만들었다.

  • import requests
    from bs4 import BeautifulSoup

    : BeautifulSoup의 기능들을 사용하기 위한 설정이다.

  • req = requests.get(http://sj.hongik.ac.kr/site/food/food_menu.html)

    크롤링 할 홈페이지를 지정하고 요청하는 부분이다(HTTP GET Request).

  • html = req.text

    HTML 소스를 가져오는 부분이다. 실제로 print해보면 모든 웹페이지를 즉, html 태그까지 모조리 다 보여준다.

    하지만 이거는 html 문법이지 Python 문법이 아니다. 즉, Python 입장에서는 그냥 어떤 문자열이라고밖에 인식하지 못한다.

  • soup = BeautifulSoup(html, ‘html.parser’)

    BeautifulSoup으로 HTML 소스를 Python 객체로 변환하는 부분으로 html 부분은 html 소스코드, html.parser는 어떤 parser를 이용할지 명시해주는 부분이다. 이따가 뒤에 내용들을 추가하면서 다시 설명하겠다. 이 부분으로 인해 Python이 문자열로만 인식했던 HTML 소스를 우리가 이해하는 HTML 태그나 구조들처럼 인식할 수 있는 것이다.

이제 기본적인 구성이 끝났다. 여기에 내가 하고자 하는 것을 추가해주면 된다. 필자는 홈페이지에서 학식 메뉴를 가져오려고 한다. 학식 메뉴는 웹 페이지에서 어떻게 구성되어 있는지 살펴보자.

이런 식으로 나와 있다. 각 메뉴가 어떻게 되어 있는지 확인해보면

각 메뉴들은 이렇게 <div class=”foodmenu”> 메뉴들 </div> 이런 식으로 구성되어 있다. 그러면 BeautifulSoup에서 제공하는 기능인 select를 이용해 <div class=”foodmenu”>로 이루어진 문구들을 추출하면 메뉴들이 보이게 될 것이다. 이런 조작이 가능한 것도 BeautifulSoup가 Python이 HTML 소스를 이해하도록 객체구조를 변환해주기 때문이다.

(참고 : select는 CSS Seletor를 이용해 조건과 일치하는 모든 객체들을 List로 반환해준다.)

이를 코드로 작성하면

pooh_menu = soup.select(
    'div[class=foodmenu]'
    )

이 부분을 추가해주면 된다.

for menu in pooh_menu:
    print(menu.text)

이 부분은 크롤링이 잘 나오는지 눈으로 확인하기 위해 넣은 것이다. menu.text를 한 이유는 글자들만 뽑아내기 위함이다. 만약에 text를 쓰지 않고 print(menu)를 하면 태그까지 다 나온다.

이게 print(menu)를 한 상태의 parsing.py를 실행한 화면이다. 하지만 우리는 태그까지는 필요없기 때문에 저렇게 menu.text를 사용한 것이다. 다시 parsing.py를 실행시켜보면

$ sudo python3 parsing.py

이렇게 메뉴들이 잘 나오게 된다. 다음 포스팅에서는 이 메뉴들을 각 버튼에 맞게 어떻게 보여줄 것인지에 대해 포스팅하겠다.




참고 사이트 :

  • https://beomi.github.io/2017/01/20/HowToMakeWebCrawler/
  • https://medium.com/@mjhans83/%ED%8C%8C%EC%9D%B4%EC%8D%AC%EC%9C%BC%EB%A1%9C-%ED%81%AC%EB%A1%A4%EB%A7%81-%ED%95%98%EA%B8%B0-908e78ee09e0

,

阅读全文 »


[Django 챗봇] 2-3. 버튼 만들기(3)


저번 포스팅에서 버튼을 대략 완성시켰는데

현재는 어떤 버튼을 눌러도 모두 ‘학식 메뉴입니다’라는 메시지만 뜬다. 하지만 최종적으로는 저 버튼에 대한 응답은 모두 달라야 한다. 현재까지 짠 코드들을 살펴보면서 다음 코드들을 어떻게 짤지 구상해보도록 하자.

코드들을 보게 되면 keyboard 부분이 챗봇 화면에서 버튼으로 나타나는 형식이다. ‘도움말’, ‘내일 메뉴’를 제외한 나머지 버튼들은 학식 정보를 크롤링하여 불러올 것이기 때문에 나중에 다루기로 하고 이번 포스팅에서는 ‘도움말’, ‘내일 메뉴’ 버튼만 다뤄보도록 하자. ‘도움말’ 버튼을 누르면 필자가 만든 챗봇을 소개하는 문구, 즉, 텍스트만 넣을 것이고 ‘내일 메뉴’ 버튼을 누르면 저 버튼들이 ‘내일 A동 학식’, ‘내일 B동 학식’ 이런 식으로 나오기를 원했다. 즉, 각 버튼에 대한 응답값이 달라야 한다는 말이다. 이를 위해 다음 부분들을 추가해주자.

json 데이터들을 처리해줘야 하기 때문에 먼저 import json을 추가해 준 다음, 해당 코드들을 추가해준다.

추가해 준 변수들을 해석해보면

  • json_str = (request.body).decode(‘utf-8’)

    request 파라미터를 한글 호환이 되는 ‘utf-8’로 바꾸어 json_str에 저장한다.

  • received_json = json.loads(json_str)

    json 문자열로 되어 있는 것을 Python 타입으로 변경시켜 recevied_json에 저장한다.

  • content_name = received_json[‘content’]

    사용자가 누른 버튼에서 필드명이 content인 항목을 저장하는데, 이를 통해서 각 버튼값을 구별한다.



그 밑의 코드들을 보게 되면 if문을 통해 각 버튼값에 대한 응답을 다르게 return 하도록 했다. 그럼 코드대로 잘 나오는지 챗봇에서 확인해보자.

잘 나온다. 하지만 필자는 ‘내일 메뉴’ 버튼을 눌렀을 때 각 버튼들이 ‘내일 A동 학식’, ‘내일 B동 학식’ 이런 식으로 바뀌기를 원했다. 방법은 간단하다. 현재 if content_name = “내일 메뉴” 부분의 keyboard 부분이 “내일 메뉴” 버튼을 눌렀을 때 챗봇에서 보여지는 버튼들이다. 이 keyboard 부분을 수정하면 “내일 메뉴” 버튼을 눌렀을 때 버튼들이 바뀔 것이다. 직접 해보자.

keyboard 버튼을 수정했다. 참고로 “뒤로”는 다시 오늘 메뉴 버튼으로 돌아올 수 있도록 하기 위해 만든 것이다. 구상한 대로 잘 동작하는지 챗봇을 실행시켜보자.

“내일 메뉴” 버튼을 누르니 각 버튼들이 바뀌었음을 확인할 수 있다. 각 버튼들에 대한 응답값은 이런 식으로 if문을 통해 처리해주면 된다. 다음 포스팅에서는 크롤링에 대해 알아보자.




참고 사이트 : http://codeac.tistory.com/13?category=731564

,

阅读全文 »


Service


이번 포스팅에서는 Service에 대해서 다뤄보려고 한다. 환경은 저번 포스팅과 같이 모든 노드들은 VM의 CentOS 7이다.


Service

Pod와 볼륨을 이용하여 컨테이너들을 정의한 후 해당 Pod를 서비스로 제공할때, 일반적인 분산 환경에서는 하나의 Pod로 서비스 하는 경우는 드물고, 여러 개의 Pod를 서비스하면서, 이를 로드밸런서를 이용해 하나의 IP와 포트로 묶어서 서비스를 제공한다.

Pod는 Node 내부에서 고유한 IP를 가지지만 동적으로 생성이 되고, 장애가 생기면 자동으로 restart 되면서 IP가 바뀌게 된다. 즉, 쉽게 생성/삭제되는 특성 상, 로드밸런서에서 Pod의 목록을 지정할 때는 IP주소를 이용하는 것은 어렵고 또한 오토 스케일링으로 인하여 Pod 가 동적으로 추가 또는 삭제되기 때문에, 이렇게 추가/삭제된 Pod 목록을 로드밸런서가 유연하게 선택해 줘야 한다. 그래서 사용하는 것이 Pod 포스팅에서 언급했던 라벨(label)과 라벨 셀렉터(label selector)이다.

Service(서비스)를 정의할 때, 어떤 Pod를 서비스로 묶을 것인지를 정의하는 것을 label selector라고 한다. 각 Pod를 생성할 때 메타데이타 정보 부분에 label을 정의할 수 있다. Service는 같은 Cluster 내에서 Pod가 어떤 Node에 생성되었는지와 관계 없이 label selector에서 특정 label을 가지고 있는 Pod를 선택하여 서비스에 묶게 된다. 이들은 Service 오브젝트를 통해 생성하고 활용할 수 있다.

Service 오브젝트의 type에는 다음과 같은 종류가 있다.

  • Cluster-IP

    type을 지정해주지 않으면 Cluster-IP가 디폴트로 설정이 되며 해당 서비스에 클러스터 IP(내부 IP)를 할당한다. 클러스터 내부에서는 접근이 가능하지만 클러스터 외부에서는 외부 IP를 할당받지 못했기 때문에 접근이 불가능하다. 쉽게 생각하면 배포된 노드에만 적용된다고 생각하면 된다. 한 번 실험해보도록 하자.

    현재 pod.yml이다. 이를 Cluster-IP로 노출시켜보자(Pod 생성하는 과정은 생략하겠다).

    kubectl expose pods [Pod명] –name=[Service name] –type=ClusterIP

    이 역시 물론 Service.yml로 서비스할 수도 있다.

    kubectl expose pods pooh-db-nginx-pod –name=pooh-pod-service –type=ClusterIP

    Service 타입이 Cluster-IP임을 알 수 있다. 한 번 마스터 노드에서 Cluster-IP로 curl을 이용해 nginx를 띄워보자.

    curl 10.103.210.38:80

    역시나 되지 않는다. 그러면 배포된 노드에서 똑같이 실행해보자.

    curl 10.103.210.38:80

    worker-node2에 배포했기 때문에 worker-node2에서 똑같이 해보니 잘 됐다.

  • NodePort

    각 Node에서 static 포트를 노출해 외부에서 접근이 가능하고 클러스터 외부에서 :의 형태로 request가 가능하다. 이 방식은 클러스터 IP로만 접근이 가능한 것이 아니라, 모든 노드의 IP와 포트를 통해서도 접근이 가능하다. Service.yml을 보면서 설명하겠다.

    • apiVersion

      이 스크립트를 실행하기 위한 쿠버네티스 API 버전으로 보통 v1을 사용한다.

    • kind

      리소스의 종류를 정의하는 곳으로 Service를 정의하려고 하기 때문에, Service라고 넣는다. 어떤 오브젝트냐에 따라 정의가 달라진다.

    • metadata

      이 리소스(Service)의 라벨(Label)이나 리소스의 이름 등 각종 메타 데이타를 넣는다. 여기서는 Service의 이름을 pooh-pod-service라고 정의했으며 label을 app: pooh-service-label이라고 정의하였다.

    • spec

      이 리소스(Service)에 대한 상세한 스펙을 정의하는 곳으로 그 밑은 spec에 대한 상세 내용들이다.

    • spec.ports

      Pod가 관리할 컨테이너를 정의하는 부분이다.

    • spec.ports.name

      Service 오브젝트가 관리할 포트의 이름을 정의한다. 이름을 정의해주지 않으면 오류가 발생하므로 반드시 정의해줘야 한다.

    • spec.ports.port

      Service 오브젝트가 사용하는 포트번호이다. 외부 사용자가 Serivce 오브젝트에 할당되는 IP와 함께 이 포트번호로 접속하면 서비스하고 있는 Pod에 접근할 수 있다.

    • spec.ports.protocol

      통신할 때 사용할 프로토콜을 정의해주는 부분이다.

    • spec.port.targetPort

      Pod 내의 각 컨테이너들이 사용할 포트들을 의미한다. Pod.yml에서 컨테이너 부분에 지정해 준 포트랑 같다.

    • spec.port.nodePort

      이는 Pod가 배포된 노드의 IP에 이 nodePort를 이용하면 배포된 Pod에 접근할 수 있다. 이렇게 nodePort로 사용하면 직접 설정해줄 수 있지만 이를 쓰지 않으면 알아서 할당해준다.

    • spec.selector

      이 부분이 Pod 포스팅에서 잠깐 언급했던 라벨 셀렉터(label selector)이다. Service 오브젝트가 이 라벨 셀렉터로 해당 라벨을 찾아 이 라벨을 가진 Pod들을 서비스 하는 것이다. 밑에 app: pooh-db-nginx-app이라고 되어있는데 app: pooh-db-nginx-app인 라벨을 가진 Pod를 서비스하겠다는 의미이다.

    • spec.selector.app

      이 부분을 가지고 서비스할 Pod(또는 Pod 그룹)를 찾는다. 즉, 라벨이 app: pooh-db-nginx-app인 Pod를 찾는 것이다. 또한 Pod에서 app: pooh-db-nginx-app이라서 spec.selector.app인 것이지 host: pooh-db-nginx-host였으면 spec.select.host가 된다.

    • spec.type

      Service 오브젝트의 type을 정의해주는 부분으로 ClusterIP, NodePort, LoadBalancer, ExternalName 등이 있다.

    Service.yml의 전체 구조를 보자. 아래와 같이 pooh-pod-service라는 서비스를 NodePort 타입으로 선언을 하고, nginx의 nodePort를 30001로 설정하면 아래 설정에 따라 클러스터 IP의 8080포트로도 접근이 가능하지만, 해당 노드(worker-node2)의 30001 포트로도 서비스에 접근할 수 있다. 이를 그림으로 나타내보면

    위 그림처럼 적용되는지 직접 확인해보자. nginx가 보여지기 쉬우니까 nginx로 하겠다.

    1) 클러스터 IP의 8080포트로 접속

    클러스터 IP는 배포된 노드에만 적용된다 했으므로 현재 배포된 곳인 worker-node2에서 curl을 이용해 해당 클러스터 IP와 포트번호를 입력하면 잘 나오는 것을 확인할 수 있다.

    2) 노드의 30001포트로 접속

    /etc/hosts에 worker-node2의 IP를 등록해놨기에 저렇게 이름을 쓴 것이고 원래는 노드의 IP를 입력하면 된다. 각 노드의 IP는 kubectl get nodes -o wide로 확인할 수 있고 Pod가 배포된 노드의 IP를 확인하고 싶으면 kubectl get pods -o wide를 입력해주면 된다. 포트번호 30001과 함께 입력해주면 잘 나오는 것을 확인할 수 있다.

    번외) 갑자기 이런 의문이 들었다. Service는 라벨 셀렉터를 통해 Pod를 서비스한다. 만약 worker-node1에 배포된 Pod A와 worker-node2에 배포된 Pod B가 모두 aa라는 라벨을 가지고 있는 상태에서 Service가 aa라는 라벨을 가진 Pod를 서비스한다면 2개의 노드에 서비스가 되고 있는 상태이다. 이 서비스를 aa라는 라벨을 가지면서 동시에 특정 노드(worker-node1이나 worker-node2)에 있는 Pod만 서비스할 수 있는지가 궁금했다는 의미이다. 그래서 pod.yml의 옵션처럼 service.yml에도 nodeSelector라는 옵션을 줘서 실행해봤다.

    이렇게 해주고 Service 오브젝트를 생성해봤는데

    nodeSelector는 unknown field, 즉, 알 수 없는 필드라고 나온다. 이렇게는 안 되는 것을 확인할 수 있었다.

  • LoadBalancer

    특정 클라우드 provider(GCE/AWS)의 LoadBalancer에게 Service 오브젝트를 노출시키는 방법으로 보통 클라우드 벤더에서 제공하는 설정 방식이며 외부 IP를 가지고 있는 로드밸런서를 할당한다. AWS나 Google Cloud Engine과 같은 퍼블릭 클라우드에 Kubernetes가 설치된 경우에는 Loadbalancer 방식으로 설정해주면 좋다. 물론 퍼블릭 클라우드가 아니어도 LoadBalancer를 구매한 서버라던지 직접 LoadBalancer를 만든 서버에도 설정해 줄 수 있고, 그런 설정이 없어도 LoadBalancer 방식으로 서비스를 할 수는 있다. 그런 것들에 상관없이 일단 만들어보면 이 방식 역시 외부 IP를 가지고 있기 때문에, 클러스터 외부에서 접근이 가능하다.

    (※ 로드밸런싱(Load Balancing) : 하나의 인터넷 서비스가 발생하는 트래픽이 많을 때 여러 대의 서버가 분산처리하여 서버의 로드율 증가, 부하량, 속도 저하 등을 고려하여 적절히 분산처리하여 해결해 주는 서비스)

    이 역시 외부에서 접근이 가능하기 때문에 맨 밑의 type만 LoadBalancer이고, 나머지는 NodePort와 같은 방식으로 설정해 준 다음 서비스를 해주면 된다.

    이 역시 노드의 IP로도 클러스터의 IP로도 접근할 수 있다.

    1) 클러스터 IP의 8080포트로 접속

    2) 노드의 30001포트로 접속

    하지만 이렇게 LoadBalancer를 제공하는 클라우드의 IP를 설정해주지 않고 type만 LoadBalancer로 지정해주면 type이 NodePort인 경우와 다를 게 없다. 그래서 실제로 클라우드의 LoadBalancer를 이용할 때는 이런 식으로 지정해줘야 한다.

    111.111.111.111이 LoadBalancer를 제공하는 클라우드의 IP이다. 추후에 이용할 클라우드가 있으면 ExternalName 방식과 함께 다시 포스팅하도록 하겠다.




참고 블로그 :

  • http://bcho.tistory.com/1262?category=731548
  • http://tech.cloudz-labs.io/posts/kubernetes/getting-start/
  • https://kubernetes.io/docs/concepts/services-networking/service/

阅读全文 »


Unknown 상태의 Pod 삭제하기


Pod가 생성되는 도중에 또는 잘 생성되었다가 어떤 이유 때문에 저렇게 Unknown 상태가 되는 경우가 있다. 그래서 Pod를 삭제하려고 했는데 deleted라는 메시지는 뜨면서 그 이후로 응답이 없다. 그래서 Ctrl + C로 빠져나왔고 kubectl delete로도 지워봤지만 역시나 되지 않았다. 그림에는 없지만 저 상태에서 kubectl get pods를 입력하면 Pod가 삭제되지 않고 남아있다. 필자의 경우는 왜 저렇게 Unknown 상태가 되었는지 살펴보면

kubectl describe pod(s) [Pod명] 을 입력하면 현재 Pod 상태를 자세하게 볼 수 있다. 현재 pooh-db-nginx-pod는 Terminating, 즉, 종료된 상태이며 Reason(이유)는 NodeLost(노드를 잃어버림)이다. 그 밑의 Message를 보면 pooh-db-nginx-pod를 실행하는 노드인 worker-node2가 unresponsive(응답이 없음) 상태라고 나온다. 메시지대로 필자의 경우 worker-node2에 Pod를 생성했고 처음에 Running 상태였으나 worker-node2의 Pod가 삭제되지 않은 채로 worker-node2가 꺼져버렸기 때문에 저런 에러가 떴던 것이다. 이 외에도 여러 이유가 있을 수 있다. 중요한 건 Unknown의 이유가 아니라 Unknown 상태 자체이다. 이럴 때는 Pod 삭제를 못하는 것일까? 아니다. 다음처럼 해주면 된다.

kubectl delete pod(s) [Pod명] –grace-period=0 –force

이렇게 해주면 해당 Pod가 잘 삭제된다. kubectl get pods로 확인해보면 Pod가 잘 삭제되었음을 확인할 수 있다.




참고 사이트 :

  • https://github.com/kubernetes/kubernetes/issues/43279

阅读全文 »


[Django 챗봇] 2-2. 버튼 만들기(2)


저번 포스팅에서 keyboard 함수를 통해 버튼을 만들어 로컬에서 띄우는 것까지 해봤다. 하지만 카카오톡으로 서비스를 하려면 외부에서 접근이 가능해야 한다. 그러면 외부에서 접속되게 해보자.
이전 포스팅에서도 언급했지만 필자는 VM을 서버로 두었다고 했다. 따라서 외부에서 필자가 사는 건물의 IP로 접속할 때 필자의 VM으로 접속되도록 포트포워딩을 시켜주면 된다. 필자의 VM은 bridge 방식으로 설정을 해주었고, VM에서 Django 서버를 띄운 다음, 건물의 IP의 특정 포트를 Django 서버를 띄워준 포트로 연결시켰다. 좀 더 자세히 말하면, 건물의 IP에 8000포트로 접속하면 VM의 8000포트로 접속하도록 포트포워딩을 시켜줬다(이에 대한 내용은 ‘Django 서버 외부접속 허용 및 포트번호 변경’ 포스팅을 참고해주세요!! ).

이제 카카오톡 앱을 만들 차례이다. 만드는 방법은 ‘[PHP 챗봇] 플러스 친구 가입하기’에 자세하게 나와있다. 여기서 그 과정들은 생략하고 바로 API 테스트로 진행하겠다. API를 테스트하려면 Django 서버를 띄워줘야 한다.

$ sudo python manage.py runserver 0.0.0.0:8000

그러고 나서 앱 URL창에 ‘http://서버IP:포트번호’를 입력해 준 후 ‘API 테스트’ 버튼을 누른다.

설정이 잘 됐다면 이렇게 버튼이 잘 나오는 것을 확인할 수 있다. 외부에서 접속 허용을 해주지 않으면 저렇게 버튼이 나오지 않기 때문에 꼭 해줘야 하고 나머지 설정들을 해 준 다음 ‘API형 저장하기’를 눌러주면 끝이다. 그러면 이제 만든 앱을 추가해서 버튼이 나오는지 확인해보자.

이렇게 버튼이 잘 나옴을 확인할 수 있다. 하지만 지금은 버튼만 나오게 했지, 버튼을 눌렀을 때 응답이 나오도록 구현하지는 않았다. 이제 그 부분을 구현해야 한다. 이 때 사용하는 것이 message 함수이다.
API 가이드에 보면 전송방법은 이렇게 나와있다.

{
    'message': {
        'text': '응답내용'
    },
    'keyboard':{
        'type': 'buttons',
        'buttons': ['A동 학식(교직원 식당)', 'B동 학식', '신기숙사(새로암학사) 식당', '구기숙사(두루암학사) 식당', '내일 메뉴']
    }
}

~/pooh_app/views.py를 열어 message 함수를 추가해주자.

그 다음 keyboard 함수 때와 마찬가지로 message 함수 역시 보내야 하기 때문에 urls.py에서 url을 설정해줘야 한다. pooh_chatbot의 urls.py를 열자.

vim ~/pooh_chatbot/urls.py

아래에 표시된 내용을 추가해 준 다음 챗봇에서 버튼을 눌러 응답을 살펴보면

분명 맞게 만들어 준 거 같은데 에러가 뜬다. 왜 에러가 뜨나 봤더니

/message에 CSRF cookie not set이라고 한다. 이를 해결해주기 위해 다음을 추가해주면 된다.

이러고 다시 챗봇을 실행을 시켜보면

버튼을 누를 때마다 응답이 나온다. 지금은 하나의 응답만 만들었기 때문에 모두 같은 응답이 나오지만 다음 포스팅에서는 각 메뉴에 대한 응답이 다르도록 코드를 짜보겠다.




참고 사이트 :

  • http://codeac.tistory.com/12?category=731564

,

阅读全文 »