반응형
반응형

우선 위 에러가 발생하는 경우는 playbook이든 ansible이든 become 메소드를 이용하여 sudo 권한을 획득하려 했으나, Windows 머신 등이 inventory에 포함되어 있는 경우 sudo 가 없기 때문에 발생한다.

 

Windows 머신의 경우 become 메소드 없이도, Administrator 계정을 inventory에 넣으면 비슷한 효과가 있기 때문에 inventory에서 become 메소드를 아예 활용하지 않도록 아래와 같이 예외처리가 가능하다.

[windows:vars]
ansible_become=false

[windows]
localhost1
localhost2
localhost3

 

ansible_become을 inventory에 선언해두면, playbook에서 become이 yes로 되어 있더라도, 해당 host들은 inventory에 선언해둔 값을 우선 시 하기 때문에, 문제가 발생하지 않는다.

반응형
,
반응형

playbook 작성 시 상단에 gather_facts 여부를 지정할 수가 있다.

playbook 하나로 모든 OS에 대해서 대응을 해야 하는 경우, OS에 대한 종류가 많아 분기문이 많이 들어가야 하는 경우에 ansible_facts를 사용해서 해당 하는 서버의 내부 정보를 수집하고, 그 수집된 정보 중 OS의 정보를 가져와서 해당 값들을 분기문에 추가할 수가 있다.

이런 경우 gather_facts를 yes로 지정해 주어야 한다.

 

분기문에 추가하는 경우는 아래와 같이 심플하게 추가할 수 있다.

---
 - hosts: all
   gather_facts: yes
   tasks:
     - name: check ubuntu14
       debug:
         msg: "ubuntu 14!!!"
       when:
         - ansible_facts['distribution_file_variety'] == "Debian"
         - ansible_facts['distribution_major_version'] == "14"

 

inventory에 linux 장비들만 포함되어 있다면 크게 문제가 없이 실행이 된다.

하지만, windows 머신이 inventory에 하나라도 포함되어 있다면, gather_facts시 linux 에서만 수집할 수 있는 ansible_facts['distribution_file_variety'] 값이 없기 때문에 에러가 발생할 수 있다.

 

이런 경우에는 when 문에 아래와 같이 is defined 문을 추가해줌으로써 해결할 수 있다.

---
 - hosts: all
   gather_facts: yes
   tasks:
     - name: check ubuntu14
       debug:
         msg: "ubuntu 14!!!"
       when:
         - ansible_facts['distribution_file_variety'] is defined
         - ansible_facts['distribution_file_variety'] == "Debian"
         - ansible_facts['distribution_major_version'] == "14"

 

일반적인 프로그래밍 언어의 if문과 같은 매커니즘 이므로, is defined 문은 가장 상단에 위치해야 원하는대로 동작하게 된다.

 

이제 facts를 수집해서 분기문을 처리하는 부분까지는 완료를 했으나, 수집된 facts를 msg 등의 string에 포함시켜서 출력해야 하는 경우가 있을 수 있는데, 이런 경우 ansible에서는 jinja2 엔진을 내장하고 있기 때문에 jinja2 문법을 활용하면 된다.

 

아래는 debug모듈의 msg에 ansible에서 수집한 facts 값을 출력하는 예시를 들어보았다.

---
 - hosts: all
   gather_facts: yes
   tasks:
     - name: check ubuntu14
       debug:
         msg: "ubuntu 14!!! file_variety is {{ ansible_facts['distribution_file_variety'] }} !!"
       when:
         - ansible_facts['distribution_file_variety'] is defined
         - ansible_facts['distribution_file_variety'] == "Debian"
         - ansible_facts['distribution_major_version'] == "14"

 

 

반응형
,
반응형

Ansible같은 경우 기본적으로 ssh를 이용해서, 원격커맨드를 날리는 것이 컨셉이지만,

Windows는 기본이 ssh를 사용하지 않기 때문에, winrm을 사용해서 구성해야 한다.

 

다만, Ansible이 winrm을 이용해서 통신을 할 때 기본은 ssl을 지원하는 5986포트로 통신을 하고 있기 때문에,

Windows에 ssl 구성을 해둔적이 없다면, ssl을 이용하지 않는 5985 포트를 이용해서 구성해야 한다.

 

# inventory


[aaa:vars]
ansible_ssh_user=Administrator # 윈도우 계정 넣으면 됨
ansible_ssh_pass=admin123! # 윈도우 계정에 대한 패스워드
ansible_ssh_port=5985  # 이게 winrm 포트임, 변경했다면 변경한 포트에 맞게
ansible_connection=winrm # 제일 중요함

 

 

이렇게 구성하지 않고, 아이피 목록만 넣게 되면 ssh로 통신하게 되므로,

Windows환경을 가진 서버들을 관리하고 싶으면 저렇게 구성해서 사용이 필요함.

반응형
,
반응형

AWS에서 EC2를 생성할 때 CentOS 관련 이미지로 생성을 하면,

아마존 콘솔에서 아무리 높은 용량의 EBS를 마운트했다고해도, OS에 들어가보면 기본 Root볼륨이 8G로 잡혀있게 된다.

(Public AMI든 Private AMI든 상관없이 모두 발생)

 

Root볼륨외에 다른 디렉토리를 만들어서, 그 디렉토리에 남은 용량을 마운트하여 사용해도 되지만,

패키지 설치나 커널 등 신경쓰지 않으면 사용자가 의도하지 않는 시점에도 Root볼륨에 상주하여 용량을 꽤 잡아먹을 수 있기 때문에, 기존 Root볼륨에 합쳐서 사용하는 방법을 알아보았다.

 

혹시 Root볼륨에 붙은 디바이스명이 /dev/xvda가 아니라면, 아래 키워드는 주의해서 실행이 필요함.

아래 키워드들은 /dev/xvda의 첫번째 디바이스로 가정하고 작성된 키워드들임.

 

1. growpart 패키지 설치

$ yum install -y cloud-utils-growpart

 

2. EBS에 할당된 용량만큼 기존 용량에서 확장한다.

$ growpart /dev/xvda 1

 

 

3. 리부팅 (보통은 여기서 마무리 되지만, 마무리 안된 경우 4번으로 넘어감)

$ reboot

 

 

4. Resize 진행

$ resize2fs /dev/xvda1

 

 

반응형
,
반응형

새롭게 yaml 파일을 구성하다보면, 이것저것 테스트를 할 일이 있는데, 나중에야 의미가 있겠지만 의미 없어보이는 retry파일이 남게 되어 비활성화하는 방법을 찾아봄.


/etc/ansible/ansible.cfg 파일에 아래와 같은 내용을 추가해준다.

### /etc/ansbile/ansible.cfg ###
[defaults]
retry_files_enabled = False


반응형
,
반응형

설치 / 초기화 / flannel 까지 세팅을 했는데, kubectl get nodes에서 계속해서 NotReady라고 나온다.

$ kubectl get nodes
NAME     STATUS     ROLES    AGE   VERSION
u16-01   NotReady   master   62m   v1.13.3


시간이 지났는데도 Ready로 바뀌지 않는 경우, 뭐 때문에 NotReady라고 나오는 지 알고 싶다면, 아래와 같은 키워드를 이용하여, Condition 부분을 살펴보면 됨.

$ kubectl describe node 노드이름


(예시)

...
Conditions:
  Type             Status  LastHeartbeatTime                 LastTransitionTime                Reason                       Message
  ----             ------  -----------------                 ------------------                ------                       -------
  MemoryPressure   False   Wed, 20 Feb 2019 07:50:16 +0000   Wed, 20 Feb 2019 06:47:37 +0000   KubeletHasSufficientMemory   kubelet has sufficient memory available
  DiskPressure     False   Wed, 20 Feb 2019 07:50:16 +0000   Wed, 20 Feb 2019 06:47:37 +0000   KubeletHasNoDiskPressure     kubelet has no disk pressure
  PIDPressure      False   Wed, 20 Feb 2019 07:50:16 +0000   Wed, 20 Feb 2019 06:47:37 +0000   KubeletHasSufficientPID      kubelet has sufficient PID available
  Ready            False   Wed, 20 Feb 2019 07:50:16 +0000   Wed, 20 Feb 2019 06:47:37 +0000   KubeletNotReady              runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
...


network plugin이 없다는 에러인데, 이런 경우 /etc/cni/net.d/10-flannel.conflist 파일을 찾아서, 복사 후 /etc/cni/net.d/10-flannel.conf로 만들어주면, 재시작 등 별도의 작업 없이 바로 해결 가능.



반응형
,
반응형

사내에 국내 / 해외 모니터링을 하는 중앙 서버가 하나 있고, 해당 중앙 서버를 Web 환경에서 컨트롤 할 수 있는 Spring Boot로 개발한 어플리케이션을 하나 운영중인데, 중앙 서버가 모니터링 하는 서버들의 상태를 특정 주기마다 불러와서 보여주는 역할을 하고 있다.


해당 역할을 하는 중앙 서버의 모니터링 시스템이 어플리케이션이 아니라, 그냥 데몬이기 때문에 어플리케이션 단에서는 ssh 통신을 해서 Input Stream을 통해 콘솔에 나타나는 정보를 가져와야 한다.


ssh 통신 간 사용한 라이브러리는 jsch (http://www.jcraft.com/jsch/)를 이용하였고, In / Output Stream을 편리하게 메소드 형태로 가져올 수 있게 구현이 되어 있다.


아무런 생각없이, 아래와 같이 구현하고 몇 개월동안 별 문제 없이 운영을 했었음. (ssh 연결 과정은 생략하며, Stream 처리 부분만 명시함.)

...
// stdout이 jsch에서 가져온 InputStream임..
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));
StringBuffer sb = new StringBuffer();
String line;
while((line = reader.readLine()) != null) {
    sb.append(line + "\n");
}
...


정상적인 상황에서는 문제가 없지만, 가끔 중앙 서버와 통신하는 서버의 IP가 잘못 입력된 경우나, 없어진 서버가 생기면 시스템에 과부하가 오는 현상이 발견되었다.


디버깅을 하던 과정 중 위 코드의 치명적인 문제를 발견함.


1. 일단, BufferedReader의 readLine method가 blocking method이기 때문에, 읽을 수 있을 때까지 무한정 대기하며, 서버내의 자원을 점유한다. (이런 류의 요청이 쌓이면, OOM 등 시스템에 문제가 전파됨.)


2. 무한정 대기하다가 결국 못읽는다고 판단하면, Exception을 발생시키는데, 이 시점을 코드 레벨에서 컨트롤할 수 없음.

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

최종적으로 아래와 같이 해결함.


1. BufferedReader에서 제공하는 ready method를 이용해, 읽을 수 있는 지 먼저 검사하는 방식으로 시스템이 blocking되지 않도록 변경


2. Guava(https://github.com/google/guava)에서 제공하는 SimpleTimeLimitter를 이용하여, 코더가 Timeout을 컨트롤 할 수 있도록 구조를 변경

BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));
StringBuffer sb = new StringBuffer();
int timeoutSec = 3;
SimpleTimeLimiter limiter = new SimpleTimeLimiter();
    try {
	limiter.callWithTimeout(() -> {
            String line;
            boolean isReady = false;
            while(!isReady) {
                isReady = reader.ready();
            }
            while((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
	}, timeoutSec, TimeUnit.Seconds, false);
    }
...


이제 중앙 서버가 IP가 잘못 등록되었거나 존재하지 않는 원격 서버와의 통신을 시도해도, 3초 이상 기다리지 않고 요청을 끊어버리므로 위에서 발견된 문제는 모두 해결이 되었다.

반응형
,
반응형

daemonize 옵션을 지원하지 않는 foreground 프로세스의 경우 background 형태로 동작하는 것처럼 하려면 보통은 nohup 명령어를 사용하여 아래와 같이 실행을 하게 된다.

$ nohup program &


실행을 하고나면, 명령어를 실행한 경로에 nohup.out이라는 파일이 생기며, program 이라는 프로세스가 뿜어내는 로그들을 찍게 되는데, 이후에 해당 프로세스를 죽이거나 재실행 하기 위해서는, 프로세스를 일일히 뒤져서 pid를 알아내서 죽여야 한다.


이런 번거로움을 방지하기 위해서는, nohup 명령어로 프로세스를 띄울 때 pid를 파일로 저장해서 해당 파일이 존재하는 경우 kill을 하고 띄우는 방법을 이용하면 좋을 것 같아서, 아래와 같은 스크립트를 하나 만들어서 활용하기로 하였다.

#!/bin/bash

if [ -f program.pid ]; then
  kill -9 `cat program.pid`
fi

nohup program > nohup.out 2>&1 & echo $! > program.pid


nohup.out 파일조차 남는게 지저분해보이고 귀찮으면, nohup.out 부분을 /dev/null로 변경해주면 된다.

반응형
,
반응형

docker-compose.yml 파일을 구성할 때, 특별한 이유가 없는 한 도커 이미지 버전을 latest로 구성하게 된다.


latest로 해놨다고 지속적으로 업데이트 해주는게 아니라, 컨테이너를 구성할 당시의 지정된 image의 latest버전이기 때문에, 새롭게 latest버전이 release되면 수동으로 업데이트를 해주어야 하는데, 이 방법에 대해 알아보자.


1. 이미지 pull

$ docker-compose pull


2. 컨테이너 다시 만들기

$ docker-compose up --force-recreate --build -d


3. (선택) 기존 이미지 제거 (사용되지 않는 이미지를 한 번에 날리게 되니, 임시로 내려둔 컨테이너가 있다면 주의한다.)

$ docker image prune -f



반응형
,
반응형

python 관련한 모듈 설치 시에 easy_install이나 pip를 사용하는데, 아무 설정없이 그냥 설치하면, nodejs나 maven같이 각 프로젝트에 해당 하는 모듈을 설치하는 것이 아니라, 시스템에 global하게 설치가 되버려서, 잘 됬던 기능도 호환성 이슈에 따라 문제가 생기는 경우가 발생하게 된다.


위와 같은 문제가 발생하는 것을 막기 위해, virtualenv 라는 모듈을 설치하여, 시스템에 global하게 설치되는 방식이 아닌, 해당 프로젝트에서만 종속되는 모듈을 설치할 수 있도록 가상 개발환경 구축이 가능하다.


[설치]

일단 아래와 같은 키워드로 virtualenv를 설치해보자. (사전에 pip가 설치되어 있어야 함.)

$ pip install virtualenv


가상 환경을 구축할 폴더를 하나 만들고, virtualenv를 이용하여 가상 환경을 구축해보자.

$ mkdir virtualenv_test
$ virtualenv venv

virtualenv 명령어 실행 시 첫번째 argument는 만들어질 가상환경의 이름(alias)이다. 보통 venv, .venv 등으로 만들며 이름만 다르다면 여러개도 만들 수 있다.


가상 환경 구축이 완료되면, 해당 디렉토리에 venv (alias) 디렉토리가 생기며, 가상 환경 컨트롤을 위한 몇 가지 폴더와 스크립트가 생긴다.


이제 기본 세팅은 끝났고, 가상 환경 활성화 / 비활성화에 대해 알아보겠다.


[활성화 / 비활성화]

만들어진 폴더들을 보면, Windows 환경인 경우에 Scripts 폴더가 있고, Linux 환경의 경우 bin 폴더가 있을 것이다.

* Windows

$ ls venv/
Include/  Lib/  pip-selfcheck.json  Scripts/  tcl/


* Linux

$ ls venv/
bin  lib  local


활성화는 아래와 같은 키워드로 한다.

* Windows

$ . venv/Scripts/activate


* Linux

$ . venv/bin/activate


활성화가 완료되면, git에서 브랜치를 변경했을 때처럼 쉘 앞에 (venv) 라는 문자열이 생기며, 앞으로 pip를 통해 설치하는 모듈은 해당 가상환경에만 종속되게 설치가 된다.


주의할 점은, 비활성화를 시키지 않은 채 다른 프로젝트로 이동해 작업을 하다가 필요한 모듈을 설치하면, 활성화 당시의 가상환경에 설치가 되기 때문에 혼선이 있을 수 있어, 하나의 프로젝트에서 필요한 작업을 완료하고 나면, 비활성화를 꼭 시켜주도록 한다.


비활성화는 아무 디렉토리에서나 아래 명령어를 통해 가능하다.

(venv)$ deactivate


이렇게 가상환경 구축을 이용해서 작업을 하다가, 최종적으로 원격 서버에 배포를 진행하기 위해서는 가상환경에서 설치했던 모듈들을 파일로 만들어줄 필요가 있다.


그럴땐 아래와 같은 키워드로 설치된 패키지 목록을 파일로 만들어준다.

(venv)$ pip freeze > requirements.txt


이렇게 해두면, 원격 서버에서 소스를 올린 후에 아래와 같은 커맨드로 한 번에 모듈을 설치할 수 있게 된다.

$ pip install -r requirements.txt


반응형
,
반응형