Встановлення та отримання стану виходу/входу за допомогою MQTT шини

Коли ви маєте вимикач та лампу, нічого вам не заважає контролювати напряму стан цієї лампи за допомогою вимикача, але ми не шукаємо простих рішень.

Маємо:

  • вимикач
  • лампа
  • openhab

Бажаємо: підключити лампу та вимикач до openhab, вимикати та вмикати лампу за допомогою цього вимикача.

Апаратне забезпечення: беремо raspberry pi (можна зробити на ESP8266 але є бажання не мати WiFi там, де його можна не мати), на вході зробимо просту схему для захисту (ну якщо вимикач буде далеко, а неподалік буде блискавка), на виході MOC3063 (навіть без додаткового сімістора вона позволяє керувати сучасними лампами).

Виходить щось таке:

 

 

 

 

 

Програмне забезпечення: spring-boot java додаток. Додаток можна знайти тут.

Openhab конфігурація:  повинна мати такий запис у секції items:

Switch PI1_SW_1 { mqtt=">[broker:/java-switch/gpio/out/GPIO 2:state:OFF:LOW],>[broker:/java-switch/gpio/out/GPIO 2:state:ON:HIGH],<[broker:/java-switch/gpio/in/GPIO 0:state:MAP(HILO.map)]" }

Як наслідок, воно працює. Затримка між командою зі станом та між та командою на зміну стану є приблизно 50 мс.

Як додаток можна подивитись (та змінити) поточний стан за допомогою веб інтерфейсу:

 

Видалення файлів з git

Якщо ви помилково додали до репозитарію файл чи теку, то її можна видалити. (Це навіть можна зробити якщо ви це зробили не помилково).

Для цього можна використати команду

git filter-branch --force --index-filter "git rm --cached --ignore-unmatch **/name/*.*" --prune-empty HEAD
git push -u origin --force

Перша команда видалить усі файли у теці name, друга відправить ці зміни до центрального репозіторію та перезапише усе, що там є.

 

Розумна хатинка – OpenHAB, підрахунок електроенергії

Трохи раніше ми налаштували OpenHAB записувати усі історичні данні до бази даних.

На даний момент тестова система керує розеткою, до якої підключено бойлер.
Розетка може вимірювати струм, значення струму заноситься до бази даних.
Щоб підрахувати скільки електроенергії було витрачено бойлером підключимось напряму до бази даних. Ми використовуємо базу H2, тому можна підключитись з браузера, просто перейшовши за адресою – http://192.168.0.6:8082/
База має просту структуру. Одну таблицю ITEMS та таблиці ITEMXXXX які містять дані.
Зробимо запрос

SELECT * FROM ITEMS

на що отримаємо відповідь:

з відповіді можна побачити, що  значення з датчика BATHROOM_BOILER1_CURRENT_1 пишуться у таблицю ITEM0007.

Усі таблиці мають дуже простий формат – стовпчики TIME та VALUE. Для підрахунку кількості витраченої електроенергії виконаємо такй запрос:

SELECT formatedtime,
       sum((previous/1000.0)*230*TIMESTAMPDIFF(SECOND, prevtime, currenttime)*0.000277778/1000.0) AS power
FROM
  (SELECT @prevtime AS prevtime,
          SET(@prevtime,TIME)currenttime,
          FORMATDATETIME(TIME, 'yyyyMM') AS formatedtime,
          @prev AS previous,
          SET(@prev,value)CURRENT
   FROM ITEM0007
   ORDER BY TIME) AS TBL
GROUP BY formatedtime
ORDER BY formatedtime

Для простоти будемо вважати, що напруга у мережі постійна та дорівнює 230В.
У результаті отримаємо кількість споживаної електроенергії по місяцям:

Якщо переписати запроси щоб отримати суми по днях, експортувати ці данні у excel то можна отримати такий графік:

OpenHAB – моніторінг напруги у мережі

Вимоги

  • Openhab на ubuntu
  • Back-UPS XS 1100CI (можна використовувати будь-який інший, але в мене саме такий)

Підготовка

Встановлення Apcupsd

Інструкцію можна знайти наприклад за цим посиланням.

Як результат ми повинні мати змогу запустити команду apcaccess, результат виводу котрої ми й будемо використовувати

pi@openhab:/etc/openhab2 $ apcaccess
APC      : 001,036,0868
DATE     : 2017-07-15 12:12:51 +0300
HOSTNAME : openhab
VERSION  : 3.14.12 (29 March 2014) debian
UPSNAME  : openhab
CABLE    : USB Cable
DRIVER   : USB UPS Driver
UPSMODE  : Stand Alone
STARTTIME: 2017-07-15 11:02:42 +0300
MODEL    : Back-UPS XS 1100CI
STATUS   : ONLINE
LINEV    : 236.0 Volts
LOADPCT  : 1.0 Percent
BCHARGE  : 100.0 Percent
TIMELEFT : 269.3 Minutes
MBATTCHG : 5 Percent
MINTIMEL : 3 Minutes
MAXTIME  : 0 Seconds
SENSE    : Medium
LOTRANS  : 155.0 Volts
HITRANS  : 280.0 Volts
ALARMDEL : No alarm
BATTV    : 27.2 Volts
LASTXFER : No transfers since turnon
NUMXFERS : 0
TONBATT  : 0 Seconds
CUMONBATT: 0 Seconds
XOFFBATT : N/A
SELFTEST : NO
STATFLAG : 0x05000008
SERIALNO : XBXXXXX0XXXX
BATTDATE : 2015-03-17
NOMINV   : 230 Volts
NOMBATTV : 24.0 Volts
NOMPOWER : 660 Watts
FIRMWARE : 920.T4 .I USB FW:T4
END APC  : 2017-07-15 12:12:51 +0300

 

Підготовка OpenHAB

Відкриємо панель конфігурування OpenHAB та встановимо

RegEx Transformation

Конфігурування OpenHAB

things

Створимо файл /etc/openhab2/things/system.things

Thing exec:command:apc [command="/sbin/apcaccess -p LINEV", transform="REGEX((\\d+.\\d+).*)", interval=5, timeout=1, autorun=true]

items

Створимо файл /etc/openhab2/items/system.items

String SYSTEM_APC_VOLTAGE "[%s]" (SYSTEM) {channel="exec:command:apc:output"}
Number SYSTEM_APC_VOLTAGE_DOUBLE "Напруга [%.1fВ]" <energy> (SYSTEM)

Item SYSTEM_APC_VOLTAGE_DOUBLE треба через те, що exec повертає строку, а нам треба число. Створимо правило для перетворення строки до числа

rules

Створимо файл /etc/openhab2/rules/system.rules

rule "Convert System APC voltage"
when
  Item SYSTEM_APC_VOLTAGE changed
then
   SYSTEM_APC_VOLTAGE_DOUBLE.postUpdate(SYSTEM_APC_VOLTAGE.state.toString)
end

persist

Створимо конфігурацію для зберігання напруги до бази даних. Додамо строку

        SYSTEM_APC_VOLTAGE_DOUBLE : strategy = everyMinute

у файл /etc/openhab2/persistence/jdbc.persist

sitemap

Створимо графік для відображення історічних даних

Додамо строку

Chart label="Voltage" item=SYSTEM_APC_VOLTAGE_DOUBLE period=12h refresh=300

у файл /etc/openhab2/sitemaps/house.sitemap

Як результат маємо такий графік:

 

 

 

Розумна хатинка – OpenHAB, зберігання історії

У цієї статті мова піде про те, як зберігати історичні дані та потім строїти графіки. Це дає змогу потім ці дані аналізувати. Наприклад кондиціонер, обігрівач, нагрівач води витрачає більше електроенергії коли увімкнений постійно чи тільки коли треба. Та інше.

Для зберігання даних будемо використовувати базу даних H2.

Встановлення бази даних

Завантажимо архів та розпакуємо у теку наприклад /opt/h2v3

Створимо теку для бази даних: /opt/h2/db

Для запуску створемо скріпт  /opt/h2v3/bin/start.sh

#!/bin/sh
cd /opt/h2v3/bin/
java -cp ./h2-1.3.175.jar org.h2.tools.Server -web -webPort 8081 -webAllowOthers -tcp -tcpPort 8082 -tcpAllowOthers -baseDir /opt/h2/db

Для запуску бази діних під час старту системи створимо файл /etc/systemd/system/h2.service

[Unit]
Description=H2 database
After=h2.target
Before=openhab2.service

[Service]
ExecStart=/opt/h2v3/bin/start.sh

[Install]
WantedBy=default.target

Увімкнемо цей сервіс під час старту системи за допомогою команди

sudo systemctl enable h2.service

Конфігурування OpenHAB для зберігання даних

Встановимо JDBC Persistence H2 addon

 

Додаємо шлях до бази даних у файл /etc/openhab2/services.jdbc.cfg

url=jdbc:h2:tcp://localhost:8082/openhab-db

Створимо конфігурацію для зберігання усіх значеннью Для цього створимо файл /etc/openhab2/persistence/jdbc.persist:

Strategies {
        // if no strategy is specified for an item entry below, the default list will be used
        everyMinute     : "0 * * * * ?"
        every5Minutes : "0 */5 * * * ?"
        everyHour   : "0 0 * * * ?"
        everyDay    : "0 0 0 * * ?"
        default = everyChange
}

Items {
    // persist all items once a day and on every change and restore them from the db at startup
        BATHROOM*, ROOM1* : strategy = everyChange, everyMinute, everyDay, restoreOnStartup
}

Все. OpenHAB повинен створити усі таблиці та почати записувати данні:

 

Оновимо конфігурацію сайту (додамо графік):

sitemap house label="Мій дім" {
        Frame label="Дата" {
                Text item=Date
        }

        Frame label="Ванна" {
                Switch item=BATHROOM_BOILER_1
                Text item=BATHROOM_BOILER1_CURRENT_1
                Text item=BATHROOM_BOILER1_CURRENT
        }

        Frame label="History" {
                Chart label="Current" item=BATHROOM_BOILER1_CURRENT_1 period=12h refresh=300
        }
}

У результаті отримаємо таку сторінку

 

 

 

 

Розумна хатинка – OpenHAB

Виникла в мене ідея автоматизувати квартиру. В інтернеті є безліч готових варіантівю Дуже гарний перелік ви можете знайти за цим посиланням.

У тестовому варіанті ми зробимо вмикання вимикання світлодіода, та отримання рівня температури та вологості. У якості ядра розумної хатинки будемо використовувати OpenHAB, протокол обміну – ModBus. Повна схема буде виглядати так:

 

Для повторення всього, про що піде мова далі вам необхідно мати:

Програмне забезпечення

  • AtmelStudio, WinAVR чи щось інше, що надає можливість компілювати с код для мікроконтролерів Atmel (я використовував WinAVR-20100110)
  • OpenHAB

Апаратне забезпечення

  • Будь-що з підтримкою java
  • USB to RS2485 converter
  • AVR програматор
  • AT90S8515 (ви можете використовувати будь який інший, в мене просто є купа таких)
  • кілька резисторів 4К7
  • кнопка
  • світлодіод
  • монтажна плата
  1. Встановлення та конфігурація OpenHAB

Для встановлення OpenHAB його треба скачати та розпакувати. На цьому інсталяція завершена та можна починати його конфігурувати.

Конфігурування розширення Modbus

Для цього треба створити файл conf/services/modbus.cfg

#LEDS on Device 2
modbus:serial.slave5.connection=COM3:115200:8:none:1:rtu:35:2000:none:none
modbus:serial.slave5.id=2
modbus:serial.slave5.start=0
modbus:serial.slave5.length=8
modbus:serial.slave5.type=coil

#BUTTONS on Device 2
modbus:serial.slave6.connection=COM3:115200:8:none:1:rtu:35:2000:none:none
modbus:serial.slave6.id=2
modbus:serial.slave6.start=0
modbus:serial.slave6.length=8
modbus:serial.slave6.type=discrete

У моєму випадку AVR було підключено до порта COM3

Створюємо items.

Для цього треба створити файл  conf/items/modbus.items

Group  ROOM1

Contact ROOM1_SW_LIGHT_BTN_2 "Light Button 2" (ROOM1) {modbus="slave6:1"}

Switch ROOM1_SW_LIGHT_LAMP_2 "Світло 2" <light> (ROOM1) {modbus="slave5:2"}

Створюємо sitemap

sitemap test label="Мій дім" {
	Frame label="Комната 1" {
		Switch item=ROOM1_SW_LIGHT_LAMP_2

	}

}

Після цього треба сконфігорувати Classic-UI

Створюємо правило щоб обробляти команди від кнопки

rule "Light room 2 ON"
when
  Item ROOM1_SW_LIGHT_BTN_2 changed from OPEN to CLOSED
then
    if (ROOM2_SW_LIGHT_LAMP_2.state!=ON)      
        ROOM1_SW_LIGHT_LAMP_2.sendCommand(ON)
    else
        ROOM2_SW_LIGHT_LAMP_2.sendCommand(OFF)
end

 

2. Створюємо апаратну частину.

Принципова схема

Повинно вийти щось таке:

 

3. Компілюємо програму

 
#define clientAddress 0x02

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include "yaMBSiavr.h"

#define LIGHLCD PC7
#define LIGHLCDDDR DDRC
#define LIGHLCDPORT PORTC


volatile uint8_t instate = 0;
volatile uint8_t outstate = 0;
volatile uint16_t inputRegisters[4];
volatile uint16_t holdingRegisters[4];

void timer0100us_start(void) {
	TCCR0|=(1<<CS01); //prescaler 8
	TIMSK|=(1<<TOIE0);
}

/*
*   Modify the following 3 functions to implement your own pin configurations...
*/
void SetOuts(volatile uint8_t in) {
	PORTD|= (((in & (1<<3))<<4) | ((in & (1<<4))<<1) | ((in & (1<<5))<<1));
	PORTB|= (((in & (1<<0))<<2) | ((in & (1<<1))) | ((in & (1<<2))>>2));
	in=~in;
	PORTB&= ~(((in & (1<<0))<<2) | ((in & (1<<1))) | ((in & (1<<2))>>2));
	PORTD&= ~(((in & (1<<3))<<4) | ((in & (1<<4))<<1) | ((in & (1<<5))<<1));
}

uint8_t ReadIns(void) {
	uint8_t ins=0x00;
	ins|=(PINC&((1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)));
	ins|=(((PIND&(1<<4))<<2)|((PIND&(1<<3))<<4));
	return ins;
}

void io_conf(void) { 
	/*
	 Outputs: PB2,PB1,PB0,PD7,PD5,PD6
	 Inputs: PC0, PC1, PC2, PC3, PC4, PC6, PD4, PD3
	*/
	DDRD=0x00;
	DDRB=0x00;
	DDRC=0x00;
	PORTD=0x00;
	PORTB=0x00;
	PORTC=0x00;
	PORTD|=(1<<0);
	DDRD |= (1<<2)|(1<<5)|(1<<6)|(1<<7);
	DDRB |= (1<<0)|(1<<1)|(1<<2)|(1<<3);

	// set ports as output
	LIGHLCDDDR |= (1<<LIGHLCD);

}

ISR(SIG_OVERFLOW0) { //this ISR is called 9765.625 times per second
	modbusTickTimer();
}

void modbusGet(void) {
	if (modbusGetBusState() & (1<<ReceiveCompleted))
	{
		LIGHLCDPORT |= _BV(LIGHLCD);
		//PUMPPORT &= ~_BV(PUMP);
		switch(rxbuffer[1]) {
			case fcReadCoilStatus: {
				modbusExchangeBits(&outstate,0,8);
			}
			break;
			
			case fcReadInputStatus: {
				volatile uint8_t inps = ReadIns();
				modbusExchangeBits(&inps,0,8);
			}
			break;
			
			case fcReadHoldingRegisters: {
				modbusExchangeRegisters(holdingRegisters,0,4);
			}
			break;
			
			case fcReadInputRegisters: {
				modbusExchangeRegisters(inputRegisters,0,4);
			}
			break;
			
			case fcForceSingleCoil: {
				modbusExchangeBits(&outstate,0,8);
				SetOuts(outstate);
			}
			break;
			
			case fcPresetSingleRegister: {
				modbusExchangeRegisters(holdingRegisters,0,4);
			}
			break;
			
			case fcForceMultipleCoils: {
				modbusExchangeBits(&outstate,0,8);
				SetOuts(outstate);
			}
			break;
			
			case fcPresetMultipleRegisters: {
				modbusExchangeRegisters(holdingRegisters,0,4);
			}
			break;
			
			default: {
				modbusSendException(ecIllegalFunction);
			}
			break;
		}
	}
}

int main(void)
{
	io_conf();
	sei();
	modbusSetAddress(clientAddress);
	modbusInit();
    wdt_enable(7);
	timer0100us_start();

	holdingRegisters[0] = 0x0A;
	holdingRegisters[1] = 0x0B;
	holdingRegisters[2] = 0x0C;
	holdingRegisters[3] = 0x0D;
    while(1)
    {
		wdt_reset();
	    modbusGet();
    }
}

Для компілювання вам буде потрібна бібліотека ModBus.

Після компіляції прошиваємо мікроконтролер, та запускаємо усе. Повинно вийти щось таке:

Якщо щось не працює вам може допомогти

QModBus – додаток для тестування роботи шини. Дозволяє надсилати команди пристроям та дивитись відповіді.

Після міграції всього на Raspberry Pi можна отримати таку конфігурацію:

2 пристроя на шині ModBus, OpenHAB на Rasbrerry Pi.

Один має датчик температури та вологості, лампу та кнопку. Інший має тільки кнопку та лампу.

 

Вставлення SSTP з’єдняння на OS X

Іноді буває так, що е непоганий комп’ютер з поганою операційною системою яка не підтримує SSTP з’єднання.
Щоб все-ж таки встановити це з’єднання вам треба:
1. Встановити sstp-client
2. Виконати команду:

sudo /usr/local/sbin/sstpc --log-stderr --cert-warn --user username --password password sstp.server.com usepeerdns require-mschap-v2 noauth noipdefault defaultroute refuse-eap noccp

3. Перевірити що з’єднання встановлено:

ifconfig

4. Додати маршрути

route add 10.1.2.0/24 172.10.10.1

 

 

Windows – автоматичне додавання маршрутів при встановленні VPN з’єдняння

Нажаль не усі VPN сервери дозволяють сконфігуровати маршрути для клієнтів.  У такому разі кожного разу клієнт має додати маршрути до необхідних мереж після встановлення з’єдняння. Windows дозволяє автоматизувати цей процес. Щоб зробити це необхідно запустити powershell з дозволами адміністратора. Та виконати таку команду:

Add-VpnConnectionRoute -ConnectionName "vpn-name" -DestinationPrefix 10.78.0.00/16 -PassThru

Після цього маршрут 10.78.0.00/16 буде доданий автоматично після встановлення з’єднання з іменем vpn-name

Docker containers with Vagrant – частина друга.

Щоб запустити якись додаток у контейнері, цей додаток спочатку треба створити. Створимо простий додаток, котрий буде мати підключення до бази даних MongoDB та дасть змогу щось до неї писати та читати записане. А у наступному записі ми створемо 2 контейнери – один з базою даних, інший з додатком.

Створюємо простий додаток з використанням Spring Boot.

1. Створюємо об’єкт, котрий будемо зберігати у базі даних

package ua.net.maxx.demo.docker;

import org.springframework.data.annotation.Id;

public class Item {

	@Id
	private String id;
	private String value;

	private Item() {

	}

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

	public String getId() {
		return id;
	}

	@Override
	public String toString() {
		return "Item [id=" + id + ", value=" + value + "]";
	}

}

2. Створюємо інтерфейс для бази даних

package ua.net.maxx.demo.docker;

import org.springframework.data.mongodb.repository.MongoRepository;

public interface ItemRepository extends MongoRepository<Item, String> {

	public Item findById(String id);

}

3. Створюємо контролер

package ua.net.maxx.demo.docker;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/demo")
public class WebController {

	@Autowired
	private ItemRepository repository;

	@RequestMapping(method = RequestMethod.GET)
	public @ResponseBody Item getItem(@RequestParam(value = "id", required = true) String id) {
		return repository.findById(id);
	}

	@RequestMapping(method = RequestMethod.POST)
	public @ResponseBody Item getUpdateItem(@RequestBody Item item) {
		return repository.save(item);
	}

}

4. Створюємо клас щоб виконати додаток

package ua.net.maxx.demo.docker;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Starter {

	public static void main(String[] args) {
		SpringApplication.run(Starter.class, args);
	}
}

5. Створюємо фай конфігурації

spring:
  profiles.active: default
---
spring:
  profiles: default
  
  data:
    mongodb:
      host: db-host
      database: demo-database

6. Створюємо конфігурація для maven

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>ua.net.maxx.demo.docker</groupId>
	<artifactId>docker-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>Docker demo project</name>

	<properties>
		<java.version>1.8</java.version>
		<spring.boot.version>1.4.0.RELEASE</spring.boot.version>
	</properties>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<!-- Import dependency management from Spring Boot -->
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-dependencies</artifactId>
				<version>${spring.boot.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-mongodb</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

Тепер це можна запустити додаток за допомогою команди

clean package spring-boot:run

Тепер можна відкрити браузер та перейти за посиланням http://localhost:8080/demo/ та побачити таку відповідь:

demo-page-1

Незважаючи на помилку, усе працює нормально. У цьому додатку немає функції, яка обробляє такий запит.

7. Наступним кроком додаємо до цього проекту документацію, яка буде сгенерована автоматично за допомогою бібліотеки Swagger

додаємо наступні рядки до файлу pom.xml

		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>${swagger.version}</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>${swagger.version}</version>
		</dependency>

Та додаємо файл конфігурації Swagger

package ua.net.maxx.demo.docker;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {
	@Bean
	public Docket api() {
		return new Docket(DocumentationType.SWAGGER_2).select()
				.apis(RequestHandlerSelectors.basePackage("ua.net.maxx.demo.docker")).paths(PathSelectors.any()).build()
				.apiInfo(apiInfo());
	}

	private ApiInfo apiInfo() {
		ApiInfo apiInfo = new ApiInfo("Demo service REST API", "API for demo", "1.0", "Terms of service",
				"myeaddress@company.com", "License of API", "API license URL");
		return apiInfo;
	}

}

8. Тепер можна запустити додаток за допомогою тієї самої команди

clean package spring-boot:run

та перейти за посиланням http://localhost:8080/swagger-ui.html та навіть за допомогою Swagger спробувати відправити запит до додатка. У відповідь через деякий час ми отримаємо помилку. Це нормально, бо бази даних в нас досі немає.

demo-page-29. Наступним кроком підготуємо наш додаток для запуску у контейнері. Для цього додаємо у конфігурацію pom.xml плагіни, котри зберуть усі бублиотеки у теку /lib та створять jar файл, котрий можна запускати.

<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jar-plugin</artifactId>
				<configuration>
					<archive>
						<manifest>
							<addClasspath>true</addClasspath>
							<classpathPrefix>lib/</classpathPrefix>
							<mainClass>ua.net.maxx.demo.docker.Starter</mainClass>
						</manifest>
					</archive>
				</configuration>
				<version>3.0.2</version>
			</plugin>
			<plugin>
				<artifactId>maven-dependency-plugin</artifactId>
				<executions>
					<execution>
						<phase>install</phase>
						<goals>
							<goal>copy-dependencies</goal>
						</goals>
						<configuration>
							<outputDirectory>${project.build.directory}/lib</outputDirectory>
						</configuration>
					</execution>
				</executions>
				<version>2.10</version>
			</plugin>

10. Виконуємо команду

clean package install

після цього у теці /target можна буде побачити файл docker-demo-0.0.1-SNAPSHOT.jar та теку /lib з усіма бібліотеками, які необхідні цьому додатку.

 У наступній частині ми створимо контейнер до docker та запустимо додаток у образі, котрий було зроблено у частині першій.

Docker containers with Vagrant – частина перша.

Vagrant це додаток, який дозволяє автоматизувати створення якогось, заздалегідь сконфігурованного середовища.

vagrant-image-1Наприклад за допомогою команди

vagrant up

Vagrant знайде образ операційної системи, зробить нову віртуальну машину, встановить в неї Docker та запустить 2 Docker контейнера. Система для тестування готова

У ціїй статті буде розказано як створити контейнер Vagrant, як зробити простий додаток на Java та помістити його у docker контейнер та як запустити віртуальну машину та 2 докер контейнера у неї за допомогою однії єдиної команди.

 Вимоги

  • OS Windows 10 PRO з ввімкненим Hyper-V. (Можна використати також virtualbox чи Ubuntu з virtualbox, навіть айХрень можна, алл усі приклади наведені саме для Windows 10 + Hyper-V).
  • Java 8 + Maven
  • Vagrant (https://www.vagrantup.com/ на момент строрення цього запису актуальним був 1.8.5)
  • Остання версія Ubuntu у вигляді iso образу
  • трохи інтернету
  • Базові знання у створенні віртуальних машин за допомогою Hyper-V manager, робота з ними у Virtual Machine Connection tool
  • Базові знання у роботі з bash, sh чи іншою командною оболонкою
  • Базові знання програмування на Java

Створення контейнера до Vagrant

Vagrant контейнер  це образ операційної системи у якої буде запускатись docker та усі докер контейнери. Основні ідеї для створення контейнера було узято тут

1. Створюємо нову віртуальну машину та встановлюємо Ubuntu

  • Type: generation 1
  • RAM: 1024 Mb
  • HDD: 30-50 Gb
  • user: vagrant
  • password: vagrant

2. Відкриваємо консоль встановленої системи за допомогою Virtual Machine Connection tool

3. Встановлюємо пароль користувача root as vagrant:

sudo passwd root

4. Дозволяємо користувачу vagrant використовівати sudo без пароля:

sudo visudo -f /etc/sudoers.d/vagrant

додаємо рядки –

# add vagrant user
vagrant ALL=(ALL) NOPASSWD:ALL

5. Тестуємо чи може користувач vagrant використовувати sudo без паролю:

sudo pwd

система  не повинна спитати пароль при виконанні цієї команди.

6. Оновлюємо операційну систему

sudo apt-get update -y
sudo apt-get upgrade -y
sudo shutdown -r now

7. Встановлюємо ключ Vagrant

mkdir -p /home/vagrant/.ssh
chmod 0700 /home/vagrant/.ssh
wget --no-check-certificate \
    https://raw.github.com/mitchellh/vagrant/master/keys/vagrant.pub \
    -O /home/vagrant/.ssh/authorized_keys
chmod 0600 /home/vagrant/.ssh/authorized_keys
chown -R vagrant /home/vagrant/.ssh

8. Встановлюємо ssh сервер, якщо це не було зроблено під час встановлення ОС

sudo apt-get install -y openssh-server

9. Редагуємо файл конфігураціі ssh

sudo nano /etc/ssh/sshd_config

Додаємо чи знаходимо та знимаємо коментарій з рядку:

AuthorizedKeysFile %h/.ssh/authorized_keys

10. Перезавантажуємо ssh сервер

sudo service ssh restart

11. Встановлюємо hyper-v integration services. Деталі можна знайти за посиланням

apt-get update apt-get install --install-recommends linux-virtual-lts-xenial 
apt-get install --install-recommends linux-tools-virtual-lts-xenial linux-cloud-tools-virtual-lts-xenial

Після цього перезапускаємо віртуальну машину.

У Hyper-V management console ви повинні побачити ip адресу, яка була надана цієї віртуальній машині. Якщо це не так, то  можливо щось змінилося та треба шукати як виправити проблему. Поки ця проблема не вирішена продовжувати не можна, бо Vagrant не буде знати куди підключатися.

12. Редагуємо файл /root/.profile

sudo nano /root/.profile

Змінюємо рядок mesg n || true на tt y-s && mesg n || true

Зміна рядка актуальнф на момент написання цієї статті, можливо у подальшому це буде виправлено у Ubuntu чи у Vagrant. Навіть якщо виправлять, нічого поганого від цього не станеться.

13. Вимикаємо віртуальну машину

14. Експортуємо віртуальну машину до будь-якої теки.  У цієї теці буде створена наступна структура:

PS E:\HashiCorp\boxes\image-source\image> ls
Directory: E:\HashiCorp\boxes\image-source\image
Mode                LastWriteTime         Length Name 
----                -------------         ------ ----
d-----       05.09.2016     20:12                SnapShots
d-----       05.09.2016     20:12                Virtual Hard Disks 
d-----       05.09.2016     20:15                Virtual Machines

Теку SnapShots одразу видаляємо.

15. Готуємо вміст контейнера до Vagrant

Windows 10 має формат файлу віртуальної машини, котрий не підтримується Vagrant на час написання запису, тому такий файл треба зробити для Vagrant

У будь-яку теку кладемо файл generate_vm_xml_config.ps1 та запускаємо у powershell консолі

.\generate_vm_xml_config.ps1 -VMName "vagrant-dev-source" -OutputFile e:\box.xml

vagrant-dev-source – ім’я віртуальної машини у hyper-v

e:\box.xml – файл конфігурації віртуальної машини, його треба буде покласти до теці Virtual Machines

Також у теку в котрій знаходиться папка Virtual Machines покладемо файл metadata.json з наступним вмістом:

{
 "provider": "hyperv"
}

16. Створюємо Vagrant контейнер Запускаємо Ubuntu shell (чи будь-який shell котрий підтримує команду tar) у теці з віртуальногю машиной (у прикладу це тека E:\HashiCorp\boxes\image-source\image) та виконуємо команду:

tar cvzf ../vagrant-hyper-v.box ./*

Буде створено файл vagrant-hyper-v.box який й є контейнер для Vagrant.

17. Тестуємо контейнер

vagrant box add ubuntu64 vagrant-hyper-v.box 
vagrant init ubuntu64
vagrant up
vagrant ssh

При виконанні цих команд у Hyper-V manager ви повинні побачити нову віртуальну машину. З останньою командою ви повинні потрапити на цю віртуальну машину. Якщо це так – усе працює.

Ви можете залишити цей образ локально чи навіть викласти до публічного доступу. Для цього треба буде завести обліковий запис тут