블로그 이미지
평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴. 조대협


Archive»


 
 

마이크로 서비스 아키텍쳐와 컨테이너

조대협 (http://bcho.tistory.com)

모노리틱 아키텍처

마이크로 서비스 아키텍쳐를 이해하려면 먼저 이에 상반되는 모노리틱 아키텍쳐를 이해할 필요가 있다. 모노리틱 아키텍쳐는 전통적인 아키텍쳐 스타일로 애플리케이션이 하나의 서버에 배포 되고, 데이타 베이스도 마찬가지로 하나의 데이타 베이스에 모든 데이타를 저장하는 방식이다.


예전에 하나의 큰 서버를 놓고, 그 안에 하나의 애플리케이션으로 개발하는 방식인데, 수퍼돔과 같이 큰 머신을 하나 놓고, 오라클 데이타베이스에 모든 데이타를 저장하고, 애플리케이션 바이너리를 하나로 개발하는 방식이다. 중앙 관리된 구조에서 통제가 편리하고, 같은 솔루션을 사용한다는데 있어서 장점이 있다.

마이크로 서비스 아키텍처

마이크로 서비스 아키텍처 (Micro service architecture,이하 MSA)는 비지니스 기능 마다 애플리케이션 서버와 데이타 베이스 서버를 분리하는 방식이다.

아래 그림과 같이 상품 정보, 상품 리뷰, 상품 상세 정보 서비스를 각각 별도의 애플리케이션으로 나누고 데이타 베이스도 각각 나누는 방식이다.  


이렇게 서버를 나누는 이유는 몇가지가 있는다.

첫번째는 서비스를 개발하는 조직이 다른 경우, 각 조직에 서비스 기획,개발,운영에 대한 독립적인 권한을 부여함으로써, 각 서비스 개발을 할때 다른 서비스에 대한 의존성이 없이 빠르게 개발할 수 있게 하는 목적을 가진다.

두번째는 인프라의 변화에 의한 요인을 들 수 있는데, 기존에는 수억의 고성능 서버에 하나의 애플리케이션을 넣는 방식을 이용했다면 근래에는 x86 서버기반으로 상대적으로 저비용,저성능 서버를 더 많이 사용하는 방식을 이용하기 때문에 하나의 큰 애플리케이션을 만드는 것보다, 작은 애플리케이션으로 나눠서 여러 서버에 분산하는 것이 유리하기 때문이다.

모노리틱 아키텍쳐와 비교

모노리틱 아키텍쳐에 비해서, 마이크로 서비스 아키텍쳐는 앞에서 언급한바와 같이 서비스 개발에 대해서 독립성을 가짐으로써 개발의 속도를 높일 수 있는 장점이 있고, 이외에도 서비스별로 다른 기술을 사용할 수 있다는 장점이 있는데, 이는 서비스 특성에 따라서 적절한 기술을 사용할 수 있다는 것을 의미한다.


반드시 장점만 있는 것이 아니라, 단점도 있는데, 여러가지 기술을 혼용해서 사용하다 보니, 그 기술의 표준을 통제하기가 어렵다. 희귀한 기술로 개발을 진행한 서비스가 있을때 개발자가 팀을 떠나게 되면, 해당 서비스를 계속해서 유지하기 어려울 수 있고, 같은 기술을 사용하더라도 프레임웍이나 코드 표준화 개발 프로세스 표준화 등에서 어려움을 겪을 수 있다.

마이크로 서비스 아키텍쳐가 어려운점 중의 하나는 서비스가 증가될 수 록, 서비스간의 연계가 복잡해져서 장애가 발생했을때 어느 서비스에서 장애가 발생했는지 찾아서 조치하는 것이 어렵다는 것이다. 그래서 마이크로서비스에서는 특히 분산된 서비스간의 모니터링을 구현하는데 더 많은 노력을 기울여야 한다.

조직 구조

마이크로 서비스의 목적은 서비스를 작은 단위로 쪼겐 후에, 각 팀이 각 서비스에 대한 개발과 운영 전체에 대한 책임을 부여함으로써, 서비스 개발의 속도를 높이는데 있다. 그래서, 마이크로 서비스 아키텍쳐를 적용하는데 있어서 가장 중요한 것은 조직 구조이다.


소프트웨어 아키텍쳐와 조직문화에 대해서 재미있는 이론이 하나 있는데, 콘웨이의 법칙이다.  콘웨이의 법칙은 “소프트웨어 아키텍쳐는, 소프트웨어를 개발하는 팀의 구조를 따라간다" 라는 이론이다.

이를 재 해석하면 마이크로 서비스 아키텍쳐에서 서비스의 분류 단위는 이를 개발하는 팀의 단위가 된다.


아무리 아키텍쳐를 논리적으로 잘 설계해서 서비스별로 나눴다하더라도, 팀의 구조와 맞지 않으면, 결국에는 아키텍쳐가 무너진다. 예전에 시스템을 설계할때, 미국,캐나다,인도,한국팀으로 팀을 나눠서 일한적이 있었다. 마이크로 서비스 아키텍쳐를 도입했었는데, 개발을 진행함에 따라서 서비스 컴포넌트의 기능이 처음 설계대로 진행되지 않고 한쪽으로 몰리는 현상이 발생하게 되었다. 한국에 기획이 있었기 때문에 비지니스에 연계된 결정은 대부분 한국에서 나오게 되었고 많은 커뮤니케이션이 필요하였기 때문에, 한국팀이 상대적으로 비지니스에 연계된 중요 컴포넌트를 많이 맏게 되었다. 미국과 캐나다는 기술이 상대적으로 좋았기 때문에 기술 난이도가 높은 기능들이 미국과 캐나다 서비스로 몰리게 되었고, 시간대가 같기 때문에 커뮤니케이션이 활발하게 일어났고 결과적으로 미국과 캐나다 서비스는 타이트하게 서로 맞물리는 구조가 되었다.

인도의 경우 시간대가 다르고 커뮤니케이션의 어려움 때문에 한번 커뮤니케이션하는데 시간이 많이 소요되었고 그로 인해서 상대적으로 덜 중요한 기능이 인도팀으로 점점 배정되었다.


사례에서 볼 수 있듯이, 마이크로 서비스 아키텍쳐 설계에서 가장 중요한 점은 기술적으로 아키텍쳐를 잘 설계하는 것이 아니라, 팀의 구조를 아키텍쳐에 맞는 구조로 만들고, 그 안에 원할한 커뮤니케이션 환경을 만들어주는 것이 가장 중요하다.  

조직문화

팀을 분할해서 독립성을 부여해서 개발의 속도를 높일려면 단순하게 팀만을 분할해서는 불가능하다. 각 팀이 맏은 컴포넌트 또는 서비스에 개발에 대한 독립적인 책임을 부여하려면 권한도 같이 부여해야 한다.

기존의 모노리틱 시스템의 개발팀을 운영하는 구조가 중앙 조직이 의사 결정을 하고 전체 프로젝트를 진행하는 중앙 집중형 거버넌스 모델이었다면, 마이크로 서비스 아키텍쳐는 설계와 프로젝트 진행 각각에 대한 의사 결정을 각 서비스 팀에 내리는 분산형 거버넌스 구조가 바람직하다. 중앙팀에서 의사 결정을 받고 그에 따라 개발을 진행하면 커뮤니케이션 오버헤드가 발생하고 그로 인하여 빠른 개발이 어렵기 때문에 그 권한을 개발 주체에게 분산하는 모델이다.


의사 결정 권한을 각 팀에게 분산하려면 각 팀이 의사 결정을 할 수 있는 능력이 있는 팀 구조가 되어야 한다.

팀내에서 의사 결정을 하고 스스로 서비스를 개발할 수 있는 능력을 갖춰야 하는데, 이를 위해서는 기획,개발, 테스트 및 운영등 모든 역할이 한 팀안에 있어야 한다. 팀의 크기는 보통 5~8명 정도가 적절한데, 이를 2-피자팀이라고도 이야기 한다. (2개의 피자로 팀원들이 모두 먹을 수 있는 정도의 팀의 크기) 이 인원을 넘어서면 커뮤니케이션이 어려워지고 작으면 제대로 된 서비스를 개발하기 어렵기 때문에 이 정도 팀의 크기를 추천한다.


DEVOPS

Devops는 운영과 개발을 한팀에서 하는 모델을 말하는데, 팀이 개발과 운영을 모두 담당함으로써 개발과 운영 사이에서 오는 간극을 해결하고 개발된 시스템을 빠르게 배포하고, 운영 과정에서 얻은 노하우를 개발에 반영해서 시장의 요구 사항에 빠르게 반응 하는데 그 목적을 둔다.

개발과 운영을 한팀에서 담당함에도 불구하고,  Devops 엔지니어, SRE (Site Reliability engineer)등과 같이 기존의 운영팀이 하던 일을 하는 역할이 여전히 남아 있는데, 그렇다면 Devops로 넘어왔음에도 불구하고 이러한 역할이 계속 남아 있는 이유와 정확한 역할은 무엇일까?


앞에서도 언급했듯이 Devops는 개발팀이 개발/배포/운영을 모두 담당하는 셀프 서비스 모델이다. 셀프 서비스를 하기 위해서는 인프라가 플랫폼화 되어 있어야 한다. 개발팀이 직접 데이타 센터에 가서 서버를 설치하고 OS를 설치하고 네트워크 구성을 하기는 어렵고, 온라인으로 서버를 설치하고 네트워크를 구성할 수 있어야 하고, 무엇보다 쉬워야 한다. 인프라 구성뿐 아니라 그위에 소프트웨어를 쉽게 빌드 및  배포하고 운영 중인 시스템에 대한 모니터링이 가능해야 하는데, 이러한 인프라를 일일이 구성하기는 어렵기 때문에 플랫폼화가 되어 있어야 하는데, Devops 엔지니어의 역할은 이러한 플랫폼을 만드는 역할이 된다.



위의 그림과 같이 Devops 팀은, 시스템을 실행할 수 있는 런타임 인프라를 개발 배포하고, 런타임 시스템에 대한 모니터링과 로깅을 제공하며, 이 시스템에 자동으로 배포할 수 있는 CI/CD 플랫폼을 구축한다.

이렇게 개발된 플랫폼에 개발팀은 개발된 시스템을 스스로 배포하고 운영하는 모델이다.

이러한 모델은 구글의 SRE (Site Reliability Engineering)에서 좋은 사례를 찾아볼 수 있는데, SRE 엔지니어는 시스템이 개발된 후에, 인프라 시스템에 대한 플랫폼화 작업을 수행하고, 이 플랫폼이 완성되어 안정화 될때까지 지속적인 지원을 하며, 플랫폼에 대한 안정화 작업이 끝나면 플랫폼의 운영을 개발팀에 맏기고 다른 프로젝트를 위한 플랫폼 작업을 하는 방식이다.

컨테이너

이러한 플랫폼을 지원하기 위해서는 벤더 종속적이지 않고, 개발자가 손쉽게 운영 및 접근할 수 있는 인프라 관리 기술이 필요한데, 이런 기술로 많이 언급되는 기술이 컨테이너이다.




가상머신 (VM)의 경우에는 하이퍼바이저의 종류에 따라서, 호환이 되지 않는 경우가 있고, 무엇보다 가상 머신 이미지의 사이즈가 매우 크기 때문에 (수백~기가 이상) 손쉽게 이식하기가 쉽지 않다.

또한 하드웨어 계층 부터 가상화 하기 때문에 실행하는데 컨테이너에 비해서 상대적으로 많은 자원이 소요된다.

컨테이너의 경우 가상 머신을 사용하지 않고 호스트 OS의 커널에서 바로 실행이 된다. 실행되는 컨테이너의 OS가 호스트 OS와 다른 경우, 이 다른 부분 (알파)만을 컨테이너에서 추가로 패키징하여 실행이 된다.

예를 들어 호스트 이미지에 기능이 A,B,C가 있고, 컨테이너는 A,B,C,D가 필요하다면, 컨테이너에는 다른 부분인 D만 묶어서 패키징 하는 개념이다.  그래서 가상머신에 비해서 크기가 훨씬 작고 가상화 계층을 거치지 않기 때문에 훨씬 효율적이라고 말할 수 있다.

컨테이너 관리 솔루션

컨테이너를 소규모로 사용한다면 물리 서버를 직접 지정해서 배포하면 되지만, 대규모로 컨테이너를 운영하고자 할때는 어떤 서버에 컨테이너를 배치해야 하는 가에 대한 문제가 생긴다.


예를 들어 16 CPU, 32 GB 메모리 머신들에 컨테이너를 배포할때 컨테이너 사이즈가 2 CPU, 3 CPU, 8 CPU등 다양할 수 있기 때문에, 자원을 최대한 최적으로 사용하기 위해서 적절한 위치에 배포해야 하고, 애플리케이션 특성들에 따라서, 같은 물리 서버에 배포가 되어야 하거나 또는 가용성을 위해서 일부러 다른 물리서버에 배포되어야 하는 일이 있다. 이렇게 컨테이너를 적절한 서버에 배포해주는 역할을 스케쥴링이라고 한다.



이렇게 컨테이너 스케쥴링을 해주는 솔루션으로는 Kubernetes, Mesosphere, OpenStack 등 다양한 솔루션이 난립해서 혼돈이었는데, 작년말 (2017년말)을 기점으로 해서 쿠버네티스가 de-facto 표준으로 되어가는 형국이다. 아래 트랜드 그래프에서 보면 알 수 있듯이 쿠버네티스의 트랜드가 지속적으로 올라가서 가장 높은 것을 확인할 수 있다.



또한 주요 클라우드 벤더인 아마존,구글,애저 모두 컨테이너 관리 환경을 쿠버네티스를 지원하는 정책으로 변화된것은 물론이고 IBM이나 시스코와 같은 온프렘(on-premise) 솔루션 업체들도 경쟁적으로 쿠버네티스를 지원하고 있다.

가상 머신위의 컨테이너

보통 이런 컨테이너 환경을 운영할때 베어메탈 (하드웨어)위에 바로 컨테이너 솔루션을 올리지 않고, 가상화 환경을 올린 후에, 그 위에 컨테이너 환경을 올리는 경우가 많다.


베어메탈 위에 바로 컨테이너 환경을 올리면 성능적 이점도 있고, 계층도 줄어들어 관리도 편리한데, 왜 가상화 계층을 한번 더 두는 것일까? 이유는 컨테이너 환경을 조금더 유연하게 사용할 수 있기 때문이다. 먼저 가상 머신을 이용해서 컨테이너 환경을 isolation할 수 있고, 가상화를 통해서 자원의 수를 더 늘려서 이를 잘게 쪼게서 사용이 가능하다. 예를 들어 설명하면, 8 CPU 머신을 쿠버네티스로 관리 운영하면, 8 CPU로밖에 사용할 수 없지만, 가상화 환경을 중간에 끼면, 8 CPU를 가상화 해서 2배일 경우 16 CPU로, 8배일 경우 64 CPU로 가상화 하여 좀 더 자원을 잘게 나눠서 사용이 가능하다.



아두이노 nodemcu로 온습도계를 만들어 보자


조대협 (http://bcho.tistory.com)


nodeMCU 개발환경 설정이 끝났으면 간단한 애플리케이션을 하나 만들어보자

온습도계를 만들어보도록 한다. 온습도를 측정하여 LED로 출력하는 모듈을 개발해보겠다.

개발이 끝나고 나서 아두이노 개발환경에 대한 결론 부터 이야기 하자면, 쉽다. 대부분의 파츠들에 대한 SDK가 제공되기 때문에 손쉽게 개발이 가능하다. 단 해당 파츠에 맞는 SDK를 찾는데 들억는 시간이 더 많다.

온습도계 센서 (DTH11)

개발에 사용할 온습도계 센서는 DTH11이라는 센서이다. 아래와 같이 생겼는데, 좌측이 데이타, 가운데가 3.3V, 가장 우측이 GND이다.



온도와 습도 두개를 측정하는데 데이타 단자가 하나이다. 아날로그 신호를 핀에서 직접 읽는 것이 아니라 SDK를 사용한다.  DTH11 라이브러리는 https://github.com/adafruit/DHT-sensor-library 에서 다운 받아서 사용하면 된다. Adafruit_sensor 라이브러리에 대한 의존성이 있기 때문에, 해당 라이브러리 https://github.com/adafruit/Adafruit_Sensor 도 같이 설치하도록 한다.


라이브러리 설치가 끝났으면, DTH11 센서를 브레드 보드에 설치한다 좌측을 GPIO G6 포트에, 가운데를 3.3V, 가장 우측은 GND에 연결한다.


I2C LCD

다음 습도와 온도를 출력하기 위해서 LCD를 사용한다. 여기서 사용한 LCD는 I2C LCD로 가로 16자로 2줄 (16x2) 를 출력할 수 있는 LCD이다.


앞판은 LCD가 있고




뒤에는 아래 그림과 같이 LCD 아답터가 붙어 있다.

  • VCC는 nodemcu의 Vin 핀에 연결한다. 이 핀은 5V의 전앞을 낸다.

  • GND는 nodemcu의 GND에

  • SCA는 nodemcu D2에

  • SCL은 nodemcu의 D1핀에 연결한다.



다음 이 LCD를 사용하기 위해서는 SDK를 설치해야 하는데, https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library 라이브러리를 다운 받아서 설치한다.


코드

이제 코드를 보자

#include <LiquidCrystal_I2C.h>

#include "DHT.h"


#define LED D5            // Led in NodeMCU at pin GPIO16 (D0).

#define DHTPIN D6

#define DHTTYPE DHT11   // DHT 11


LiquidCrystal_I2C lcd(0x27, 16, 2);

DHT dht(DHTPIN, DHTTYPE);

float h,t;

void setup() {

 pinMode(LED, OUTPUT);    // LED pin as output.

 dht.begin();


 // LCD setting

 lcd.begin();

 lcd.backlight();

 lcd.print("Hello, world!");

 

 // Serial communication setting

 Serial.begin(9600);   

 Serial.print("Hello nodemcu");

}

void loop() {

 digitalWrite(LED, HIGH);// turn the LED off.(Note that LOW is the voltage level but actually

                         //the LED is on; this is because it is acive low on the ESP8266.

 delay(200);            // wait for 1 second.

 digitalWrite(LED, LOW); // turn the LED on.

 delay(200); // wait for 1 second.

 h = dht.readHumidity();

 t = dht.readTemperature();

 lcd.setCursor(0,0);

 lcd.print("Humidity:");

 lcd.print(h);

 lcd.setCursor(0,1);

 lcd.print("Temp :");

 lcd.print(t);

 Serial.print("H:");

 Serial.print(h);

 Serial.print(" T:");

 Serial.println(t);

}


DHT11 관련 코드

DHT11을 사용하려면, 입력 포트를 정의해야 하고 센서의 종류를 정의해야 한다.

#define DHTPIN D6

#define DHTTYPE DHT11   // DHT 11


DHT dht(DHTPIN, DHTTYPE);


에서 핀은 D6로 지정하고 DHTTYPE은 DHT11로 정의하였다.

다음 센서를 가동 시키기 위해서 setup() 에서


 dht.begin();


와 같이 DHT 센서를 가동 시켰다. 다음은 센서에서 온도와 습도를 읽어와야 하는데, loop() 함수내에서


 h = dht.readHumidity();

 t = dht.readTemperature();


readHumidity()와 readTemperature() 함수를 이용하여, 습도와 온도를 읽어왔다.


LCD 관련 코드

LCD를 사용하려면 초기화를 해줘야 하는데


LiquidCrystal_I2C lcd(0x27, 16, 2);


로 초기화를 해준다. 0x27는 LCD의 주소로, D1,D2 핀을 사용할때 사용하는 주소이다.핀의 위치가 바뀌면 이 주소도 변경 되어야 한다. 핀에 위치에 따라 주소가 다르거나 또는 인식이 안되는 경우가 있는데, http://playground.arduino.cc/Main/I2cScanner 를 이용하면 LCD가 연결이 되어 있는지 아닌지, 그리고 LCD의 주소를 알려준다.

다음 인자인 16,2는 가로 16자에 세로 2자 LCD 임을 정의해준다.


다음 초기화를 해줘야 하는데, setup()함수에서

 lcd.begin();

 lcd.backlight();

 lcd.print("Hello, world!");


로 초기화를 해준다. begin()은 LCD를 시작하는 것이고 backlight()는 LCD의 백라이트를 키도록 하는것이다. 글자를 출력하고 싶으면 print(“문자열")을 이용하면된다.

초기화가 끝났으면, DTH11 센서에서 읽은 값을 출력해주면된다.

윗줄에 습도 아랫줄에 온도를 출력할것인데

 lcd.setCursor(0,0);

 lcd.print("Humidity:");

 lcd.print(h);

 lcd.setCursor(0,1);

 lcd.print("Temp :");

 lcd.print(t);


윗줄 첫번째 위치 부터 습도를 출력할것이기 때문에, 출력 위치를  setCursor(0,0)으로 해서 맨 앞칸 첫줄로 지정을 해서 습도를 출력하고, 다음 온도는 setCursor(0,1)로 해서 맨 앞칸 두번째 줄 부터 출력하도록 하였다.



<그림. 완성된 모습 >

다음글에서는 이렇게 수집한 정보를 HTTP 를 이용해서 서버로 전송하는 코드를 추가해보도록 한다.


참고 자료


맥 OSX에서 nodeMCU와 Wemos D1 환경 설정하기


조대협 (http://bcho.tistory.com)


아두이노 우노로 아두이노 개발을 시작하고 서버 통신을 위해서 ESP8266 계열인 ESP01 칩을 사용했는데,  ESP01은 연결도 까다롭고 소프트웨어 시리얼을 사용해서 SDK를 찾기 어려운점도 있었다. 개발하고자 하는 내용이 대부분 서버와 통신을 하는 부분이기 때문에, 보드를 우노에서 ESP8266 을 메인 MCU로 하는 보드로 변경하였다.


후보군으로 올른것이 nodeMCU v2와 Wemos D1 보드이다.


<그림 nodeMCU v2와 Wemos D1 호환 보드>


nodeMCU의 경우에는 크기가 작고 성능이 뛰어날뿐 아니라, 널리 사용되는 보드이기 때문에, SDK나 예제를 구하기 쉬울것이라고 생각하였고, Wemos D1은 ESP8266을 포함하고 있으면서도 아두이노 우노와 유사한 레이아웃과 GPIO 핀 배열을 가지고 있기 때문에, 일반적인 개발에 좀더 편리하지 않을까 싶었다.


맥을 사용하기 때문에, OSX에 맞춰서 개발환경을 설정해야 했다.

USB 드라이버 설치

nodeMCU를 맥에 연결해도 MAC에서는 USB 포트를 인식하지 못한다. 이유는 nodeMCU와 통신할 USB 드라이버가 없기 때문에, nodeMCU는 아래 그림과 같이 USB 통신을 위해서 CP2102라는 칩셋을 사용한다. 그래서 이 칩셋을 위한 드라이버를 설치해줘야 한다.




드라이버를 https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers 에서 받아서 설치하면 된다. 이 드라이버는 Kernel Extension 이라는 형태인데 커널을 확장하는 기능이기 때문에 보안적인 제약사항을 받는다. 설치를 하더라도 바로 반영이 안되는데,  이유는 커널 확장 기능을 설치하려면 보안 승인을 해야 한다. USB 드라이버를 설치하고 나면 System > Preference에서 Security & Privacy 부분을 보면 아래와 같이 Kernel extension이 loading 되는 것이 블록 되어 있는 것을 볼 수 있다. 오른쪽의 Allow 버튼을 누르면 승인이 되고, 정상적으로 Kernel extension이 설치 된다.


제대로 설정이 되었는지를 확인하려면 OSX에서 해당 포트를 인지했는지 보면 되는데,

%ls /dev/tty.*

를 실행하면 다음과 같이


tty.SLAB_USBtoUART 이름으로 포트가 인식된것을 확인할 수 있다.


보드 추가

다음으로는 아두이노 개발환경인 Sketch에서 nodeMCU 보드 타입을 등록해야 한다.

Sketch 툴에서 Arduino > Preference 를 선택한다.

다음 아래와 같이 화면이 나오면 “Additional Boards Managers URLs”에

http://arduino.esp8266.com/stable/package_esp8266com_index.json

주소를 넣는다.


이렇게 해주면 Sketch에서 사용할 수 있는 보드의 종류가 추가로 등록된다. 다음 nodemcu를 사용하도록 보드를 선택해야 하는데, Tools > Boards 메뉴로 가서 아래 그림과 같이 node MCU v1.0을 선택한다.




통신 포트를 선택하고 다음 통신 속도를 921600으로 선택한다. 다음 제품 스펙에 맞게 아래 그림에서 “CPU Frequency”를 160Mhz로 조정하여서 실행하였다.


이제 개발 준비가 끝나고 개발을 진행하면 된다.



Wemos D1 환경 설정하기

Wemos D1 환경 설정도 크게 다르지 않다. 다만 USB 칩을 CH341칩셋을 사용하기 때문에, 맞는 드라이버를 설치해야 한다. 설치 방법은 동일하고, 드라이버는 https://wiki.wemos.cc/downloads 에서 다운로드 받을 수 있다. 보드 매니져에 보드를 추가해야 하는데, esp8266 계열이기 때문에, 앞에 추가한 보드 메니져에 이미 wemos d1이 들어가 있기 때문에, 이를 선택해서 사용하면 된다.


참고


라즈베리 파이 호환 보드 조사


라즈베리 파이를 구입해놓고 보니 단순한 소형 리눅스 머신이나. IO를 위한 GPIO 포트가 있는것 빼고는 크게 다르지 않다. 아두이노 시리즈도 호환 보드가 있는것 처럼, 라즈베리파이도 호환 보드가 있을것으로 생각하고 찾아보니 당연히 있다.

Orange PI



우분투,Debian, 라즈베리 파이 이미지 실행 가능


이름

CPU/GPU

메모리

디스크

가격

오렌지 파이 PC+

1.6Ghz 4 core

600Mhz GPU (OPEN GL ES)

1G

8G Flash

37500

오렌지파이 PC2

64bit 4 core, GPU 내장

1G

2MB Flush

40000

오렌지파이 라이트

4 core, 600MHz GPU

512M


25000


Banana PI


국내에서는 판매하는 곳이 많지 않고, 알리바바나 아마존을 통해서 구입해야 하는데, 기존 호환 보드에 비해서 8Core CPU를 탑재한 모델(12만원)이 있는등, 성능이 뛰어나다. 가격은 고가인편. 성능이 좋은편인데, 국내에서 많이 보편화 되지 않은 것은 아무래도 가격 때문인듯

Asus Tinker Board


1.8 GHZ, 4 코어 CPU, 600 Mhz GPU, 2GB 메모리, 가격대는 15만원선 국내에서는 판매하는 곳이 그리 많지 않음


비글본 블랙



1GHz Arm CPU, GPU 지원, 512M, 4G Flash, WIFI 지원으로 약 13만원선


국내에서 구매가 편리하고 가격대가 좋은 보드로는 오렌지 보드가 가성비 측면에서 가장 좋지 않은가 싶다.

라즈베리파이 3B+가 64비트 쿼드 코어 + 1G 메모리에 43000원 정도의 가격을 형성하는데.

오렌지 보드 PC+의 경우 32비트 쿼드코어 + 1G 메모리에 8G 온보드 플래시를 지원하면서 37000원정도이다.  PC2 모델의 경우 64비트 쿼드코어에 1G메모리에 플래시는 탑재하지 않고 40000원 수준이다.

크게 가격적인 차이는 없지만, 세세한 스펙상의 차이가 있기 때문에, 호환 보드를 고민해도 좋지 않을까 싶은데, 가격적인 차이가 크지 않기 때문에, 개발을 시작하는 입장에 있어서는 코드나 장비 호환성 측면에서 라즈베리 파이로 시작하는게 좋을것 같고, 소형 장비를 개발할때는 오렌지 보드를 고민하는 것도 좋지 않을까 한다.


아두이노, ESP8266,ESP 32 성능 비교


와이 파이 통신 모듈을 사용해보니, ESP8266이나 ESP32를 MCU로 하는 보드들을 메인 보드로 사용하는 경우가 많아서, 아두이노 우노나 메가와 같은 보드 없이도 이 보드들만으로도 성능이 충분할까해서 성능 비교표를 찾아보았다. 일단 결론 부터 이야기 하면, 아두이노 시리즈는 성능이 비교가 안된다. ESP32가 가장 빠르고, ESP8266 모델만 해도 상대적으로 매우 높은 성능을 낸다.



소스 : https://hilo90mhz.com/arduino-esp32-esp8266-101-speed-test-comparison-chart/


ESP01 (ESP8266)을 이용한 HTTP 통신


조대협 (http://bcho.tistory.com)


하드웨어 시리얼과 소프트웨어 시리얼

앞의 글에서 ESP01을 연결해봤는데, ESP01 연결시에 포트를 2,3번 포트를 사용하고, 코드는 SoftSerial 라이브러리를 이용하였다. 아두이노 우노의 0,1 번포트는 시리얼 통신을 위한 RX,TX 포트이다. 이를 Hardware Serial 이라고 한다. 그러면 하드웨어 시리얼 포트를 사용하지 않고 2,3번 포트를 사용한 후에 소프트웨어 시리얼 처리를 한 이유는 무엇을까?


하드웨어 시리얼 포트는 PC와 연결되어 있을때 PC와 통신을 목적으로 사용된다. 그래서 하드웨어 시리얼을 사용하지 않은것인데, ESP8266 관련 라이브러리들을 살펴보니, 대부분 하드웨어 시리얼을 사용하는 것으로 보인다. 그러면 이 문제를 어떻게 해결할것일까?


아두이오 우노 기판을 보면 TX와 RX라고 써있는 LED 가 있는데, 하드웨어 시리얼 통신을 할때만 깜빡 거린다.




PC에 연결이 되어 있을 때, AT 명령을 실행할때는 이 LED가 빛나지 않고, 코드를 업로드 할때만 빛이 난다. 즉 코드 업로드 시에만 하드웨어 시리얼을 PC가 사용한다는 것이기 때문에, 코드를 업로드한후에 ESP01 보드를 TX,RX 단자에 연결하면 하드웨어 시리얼을 사용할 수 있다.

아두이노를 이용한 HTTP 통신

아두이노에 ESP01 모듈을 연결했으면, HTTP 통신을 해보도록 한다. ESP01 (ESP8266 계열)은 TCP/UDP 스택이 이미 들어가 있기 때문에, TCP 통신은 가능하다. HTTP 통신을 하려면 TCP 프로토콜을 이용하여 HTTP 메세지를 보내주면 되는데, AT 명령어를 사용하면 된다.

AT 명령어 레퍼런스는 https://www.electrodragon.com/w/ESP8266_AT-Command_firmware 를 참고하기 바란다. AT명령을 이용하여 WIFI 네트워크에 연결하는 방법은 http://bcho.tistory.com/1283 에 설명되어 있다. WIFI 네트워크에 연결되어 있다고 가정하고 AT명령을 이용해서 HTTP 명령을 보내는 방법을 보자


HTTP  서버 연결

HTTP 요청을 보내기 위해서 먼저 웹서버에 TCP 커넥션을 연결해야 한다. TCP 커넥션 연결 명령어는 AT+CIPSTART로 다음과 같다.


AT+CIPSTART={TCP 연결 번호},”TCP”,”웹서버IP”,{포트번호}


이 명령은 해당 웹서버 IP의 포트로 연결을 하고 연결 번호(이름)를  “0”으로 지정한것이다.

210.118.92.89 서버의 80 포트로 연결하려면 Serial 콘솔에서 AT+CIPSTART=0,”TCP”,”210.118.92.89”,80 명령어를 실행하면 된다. 이 연결의 연결 번호는 0 번이 된다.

명령어 전달

서버로 연결이 되었으면, HTTP 명령어를 보내야 하는데, 명령어 전달은 AT+CIPSEND 명령어를 사용하면 된다. 명령어의 형식은 다음과 같다.


AT+CPISEND={TCP 연결번호},{보내고자 하는 메세지 길이}


예를 들어 “GET /index.html” 명령을 0번 TCP 연결로 보내고자 하면, 메세지의 길이가 15자이기 때문에,

AT+CPISEND=0,15 로 실행하고, 그 다음에

> 가 나오면 GET /index.html 을 전송하면 된다.

예제

실제 개발에서는 콘솔에서 AT 명령을 직접 입력해서 사용하는 것이 아니기 때문에, 이를 코드화 해야 하는데, 아래는 코드화 한 예제이다. (실제 개발에서는 사용하지 않을것이기 때문에 테스트는 하지 않았다)

#include <SoftwareSerial.h>


SoftwareSerial esp(2,3); //TX,RX

String SSID="{Wifi SSID}";

String PASSWORD="{Wifi password}";


void connectWifi(){

 String cmd = "AT+CWMODE=1";

 esp.println(cmd);

 cmd ="AT+CWJAP=\""+SSID+"\",\""+PASSWORD+"\"";

 esp.println(cmd);

 delay(1000);

 if(esp.find("OK")){

   Serial.println("Wifi connected");

 }else{

   Serial.println("Cannot connect to Wifi"+esp);

 }

}


void httpGet(String server,String uri){

 String connect_server_cmd = "AI+CIPSTART=4,\"TCP\",\""+server+"\",80";

 esp.println(connect_server_cmd);

 String httpCmd = "GET "+uri;

 String cmd = "AT+CIPSEND=4,"+httpCmd.length();

 esp.println(cmd);

 esp.println(httpCmd);


}

void setup() {

 // put your setup code here, to run once:

 esp.begin(9600);

 Serial.begin(9600);

 connectWifi();

 httpGet("210.192.111.122","/index.html");

}


void loop() {

 // put your main code here, to run repeatedly:


}


위의 붉은 색으로 표시된 부분을 보면 번호 4번으로 TCP연결을 열고, 4번 채널에 AT+CIPSEND 명령을 이용하여 GET /index.html 을 실행하였다.

SDK의 활용

HTTP 통신의 기본 원리를 이해하였으면, 이제 실제로 HTTP 호출을 해보자. AT 명령을 이용해도 되지만,  HTTP 명령어 종류가 많고, 연결, 메세지 전송/받기등 다양한 명령을 AT 명령으로 직접 코딩하기에는 코딩양이 많고 복잡해진다. 그래서 이런 명령을 잘 추상화하여 단순화 해놓은 SDK를 사용하는 것이 좋은데, 아두이노가 오픈 소스인 만큼, 오픈소스 기반의 SDK가 많다. 장점이기도 하지만 반대로 단점도 되는데, 아두이노나 ESP8266 펌웨어의 버전이 낮으면 동작하지 않는 경우도 있고, 또한 많은 라이브러리들이 아두이노 WIFI 실드를 기준으로 개발되었거나 또는 아두이노 → ESP8266으로 통신하는 용도가 아닌 ESP8266 기반의 아두이노 보드를 기준으로 개발된 라이브러리가 많다.

아두이노 라이브러리 추가하기

우리가 사용할 라이브러리는 WiFiESP라는 라이브러리 (https://github.com/bportaluri/WiFiEsp) 로 소프트웨어 시리얼과 하드웨어 시리얼 통신 양쪽을 모두 지원하며, HTTP 형태로 라이브러리가 잘 패키징 되어 있다. 또한 예제가 비교적 잘 정리되어 있어서 사용하기 좋다.

이런 오픈소스 라이브러리를 사용하려면 라이브러리를 설치해야 하는데, 설치 방법은 다음과 같다.

github에 배포된 코드의 경우, 해당 코드를 아래 그림과 같이 download 버튼을 이용하여 zip 파일로 다운로드 받는다.




다음 Sketch에서 Sketch > Include Library > Add .Zip Library 를 선택해서 앞에서 다운 받은 ZIP 파일을 선택하면 라이브러리로 등록되어 사용이 가능하다.




SDK를 이용하여 HTTP 호출


아래코드는 라이브러리를 추가한후에, 예제에 나온 코드를 그대로 사용한 예이다. 아두이노 2,3번 핀을 ESP01의 TX,RX 핀에 연결해서 소프트웨어 시리얼로 통신하였다. 그래서 코드 부분을 보면 SoftwareSerial Serial1(2,3)으로 소프트웨어 시리얼로 선언되어 있는 것을 확인할 수 있다.


#include "WiFiEsp.h"


// Emulate Serial1 on pins 6/7 if not present

#ifndef HAVE_HWSERIAL1

#endif

#include "SoftwareSerial.h"

SoftwareSerial Serial1(2, 3); // RX, TX


char ssid[] = "{WIFI SSID}";            // your network SSID (name)

char pass[] = "{WIFI Password}";        // your network password

int status = WL_IDLE_STATUS;     // the Wifi radio's status


char server[] = "arduino.cc";


// Initialize the Ethernet client object

WiFiEspClient client;


void setup()

{

 // initialize serial for debugging

 Serial.begin(9600);

 // initialize serial for ESP module

 Serial1.begin(9600);

 // initialize ESP module

 WiFi.init(&Serial1);


 // check for the presence of the shield

 if (WiFi.status() == WL_NO_SHIELD) {

   Serial.println("WiFi shield not present");

   // don't continue

   while (true);

 }


 // attempt to connect to WiFi network

 while ( status != WL_CONNECTED) {

   Serial.print("Attempting to connect to WPA SSID: ");

   Serial.println(ssid);

   // Connect to WPA/WPA2 network

   status = WiFi.begin(ssid, pass);

 }


 // you're connected now, so print out the data

 Serial.println("You're connected to the network");

 

 printWifiStatus();


 Serial.println();

 Serial.println("Starting connection to server...");

 // if you get a connection, report back via serial

 if (client.connect(server, 80)) {

   Serial.println("Connected to server");

   // Make a HTTP request

   client.println("GET /asciilogo.txt HTTP/1.1");

   client.println("Host: arduino.cc");

   client.println("Connection: close");

   client.println();

 }

}


void loop()

{

 // if there are incoming bytes available

 // from the server, read them and print them

 while (client.available()) {

   char c = client.read();

   Serial.write(c);

 }


 // if the server's disconnected, stop the client

 if (!client.connected()) {

   Serial.println();

   Serial.println("Disconnecting from server...");

   client.stop();


   // do nothing forevermore

   while (true);

 }

}



void printWifiStatus()

{

 // print the SSID of the network you're attached to

 Serial.print("SSID: ");

 Serial.println(WiFi.SSID());


 // print your WiFi shield's IP address

 IPAddress ip = WiFi.localIP();

 Serial.print("IP Address: ");

 Serial.println(ip);


 // print the received signal strength

 long rssi = WiFi.RSSI();

 Serial.print("Signal strength (RSSI):");

 Serial.print(rssi);

 Serial.println(" dBm");

}


접속하고자하는 서버의 주소는

char server[] = "arduino.cc";

에 arduino.cc 로 정의되어 있고


   client.println("GET /asciilogo.txt HTTP/1.1");

   client.println("Host: arduino.cc");

   client.println("Connection: close");

   client.println();


명령어를 이용해서 arduino.cc/asciilogo.txt 파일을 HTTP GET으로 읽어오게 되어있다.

읽어온 응답값을

 while (client.available()) {

   char c = client.read();

   Serial.write(c);

 }


코드를 이용해서 client.read를 통해서 한글자씩 읽어온후 화면에 출력하기 위해서 Seirial.write(c)를 이용하여 한글자씩 콘솔에 출력하였다.

실행 결과는 다음과 같다.



결론

테스트를 하면서 가장 어려웠던 점이 아두이노 우노에서 ESP01 로 소프트웨어 시리얼 통신을 하는 경우 HTTP나 WIFI SDK를 찾기가 어려웠다. 대부분 ESP8266을 MCU로 하는 보드 (예를 들어 nodeMCU, ESP12,Wemo D1/D1 mini)를 베이스로 하는 경우가 많았는데, 그도 그럴것이 개발이나 테스트 용도가 아니라 실제 디바이스로 만들어서 사용하려면 우노 + ESP01 조합은 크기도 크고 연결도 복잡해서 효용성이 없다. 네트워크 통신을 하는 시나리오등은 IOT와 같이 센서 데이타를 얻어서 서버에 전송하는 케이스이기 때문에,  하나의 소형 디바이스로 처리가 가능하다면 하나의 디바이스를 사용하는 것이 훨씬 효율적이다. (가격이나 배터리 용량면등).

다음 글에서는 실제로 센서에서 데이타를 읽어서 HTTP를 이용해서 서버에 전송하는 부분을 고민해보도록 하겠다.

참고자료