“ERROR: could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network” – docker-compose and bridge config problem.

Recently I had an error while running docker-compose.

$ docker-compose up -d --build
Creating network "courseenvironments_default" with the default driver
ERROR: could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network

After googled a bit, I tried to remove unused docker networks with:

$ docker network prune

But, no success.

After inspecting my /etc/network/interfaces file, I decided to change my network configuration:

# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback

auto br0
iface br0 inet dhcp
             bridge_ports eth0
             bridge_stp off
             bridge_fd 0
             post-up vconfig add br0 100
             post-up ifconfig br0.100 200.200.200.200/23
             post-up route add default gw 200.200.200.1
             post-up route del default gw 10.1.1.1
             post-up route add -net 10.0.0.0/8 gw 10.1.1.1
             post-up route add -net 192.168.0.0/16 gw 10.1.1.1
             post-up route add -net 172.16.0.0/12 gw 10.1.1.1
             post-down vconfig rem br0.100

I used to use this bridge in my old VirtualBox and GNS3 configurations; thus I removed it and used my VLAN tagged config directly into eth0 interface:

auto eth0
iface eth0 inet dhcp
            post-up vconfig add eth0 100
            post-up ifconfig eth0.100 200.200.200.200/23
            post-up route add default gw 200.200.200.1
            post-up route del default gw 10.1.1.1
            post-up route add -net 10.0.0.0/8 gw 10.1.1.1
            post-up route add -net 192.168.0.0/16 gw 10.1.1.1
            post-up route add -net 172.16.0.0/12 gw 10.1.1.1
            post-down vconfig rem eth0.100

After stopping the networking and applying the new conf, docker-compose could create the new network, and the build process finished without errors:

# ifdown br0
# vconfig rem br0.100
# brctl delbr br0
# service networking restart

$ docker-compose up -d --build
Building jenkinsBuilding jenkins
Step 1/8 : FROM jenkins:latest
 ---> cd14cecfdb3a
Step 2/8 : MAINTAINER Ernest Mueller <ernestmueller@theagileadmin.com>
 ---> Using cache
 ---> bc8067b1e3b2
Step 3/8 : USER root
 ---> Using cache
 ---> b0de05eb295c
Step 4/8 : RUN     apt-get update &&     apt-get install -y build-essential &&     apt-get clean &&     rm -rf /var/lib/apt/lists/*

Step 1/8 : FROM jenkins:latest
 ---> cd14cecfdb3a
Step 2/8 : MAINTAINER Ernest Mueller <ernestmueller@theagileadmin.com>
 ---> Using cache
 ---> bc8067b1e3b2
Step 3/8 : USER root
 ---> Using cache
 ---> b0de05eb295c
Step 4/8 : RUN     apt-get update &&     apt-get install -y build-essential &&     apt-get clean &&     rm -rf /var/lib/apt/lists/*

Realizando particionamento de tabelas para melhora de performance das consultas do Zabbix 2.0.5 e PostgreSQL 9.0.1

Recentemente atualizei meu NMS de Zabbix 1.8.2 (Debian Squeeze) para o 2.0.5, um dos principais motivos que me levou a esta versão foi poder realizar o particionamento de tabelas, já que o desempenho no meu cenário (128 hosts monitorados, retenção de um ano e NVPS=110) estava começando a ficar sofrível em alguns momentos.

O propósito do particionamento de tabelas é dividir a tabela history do zabbix em intervalos pré-definidos, como diário, semanal ou mensal. A escolha do intervalo a ser utilizado deve se basear na quantidade de dados inserido no banco em determinado período.

Após o particionamento será criado um novo schema onde ficaram as tabelas contendo os dados separados pelo intervalo selecionado, isso facilita bastante a busca das informações na base.

Todos os passos são realizados no PostgreSQL, tornando a solução totalmente transparente ao Zabbix.

Criação do novo schema.


CREATE SCHEMA partitions
AUTHORIZATION zabbix;

Criar a função que cria as partições:


-- Function: trg_partition()

-- DROP FUNCTION trg_partition();

CREATE OR REPLACE FUNCTION trg_partition()
RETURNS TRIGGER AS
$BODY$
DECLARE
prefix text := 'partitions.';
timeformat text;
selector text;
_interval INTERVAL;
tablename text;
startdate text;
enddate text;
create_table_part text;
create_index_part text;
BEGIN

selector = TG_ARGV[0];

IF selector = 'day' THEN
timeformat := 'YYYY_MM_DD';
ELSIF selector = 'month' THEN
timeformat := 'YYYY_MM';
END IF;

_interval := '1 ' || selector;
tablename :=  TG_TABLE_NAME || '_p' || TO_CHAR(TO_TIMESTAMP(NEW.clock), timeformat);

EXECUTE 'INSERT INTO ' || prefix || quote_ident(tablename) || ' SELECT ($1).*' USING NEW;
RETURN NULL;

EXCEPTION
WHEN undefined_table THEN

startdate := EXTRACT(epoch FROM date_trunc(selector, TO_TIMESTAMP(NEW.clock)));
enddate := EXTRACT(epoch FROM date_trunc(selector, TO_TIMESTAMP(NEW.clock) + _interval ));

create_table_part:= 'CREATE TABLE IF NOT EXISTS '|| prefix || quote_ident(tablename) || ' (CHECK ((clock >= ' || quote_literal(startdate) || ' AND clock < ' || quote_literal(enddate) || '))) INHERITS ('|| TG_TABLE_NAME || ')';
create_index_part:= 'CREATE INDEX '|| quote_ident(tablename) || '_1 on ' || prefix || quote_ident(tablename) || '(itemid,clock)';

EXECUTE create_table_part;
EXECUTE create_index_part;

--insert it again
EXECUTE 'INSERT INTO ' || prefix || quote_ident(tablename) || ' SELECT ($1).*' USING NEW;
RETURN NULL;

END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION trg_partition()
OWNER TO postgres;

Criar as triggers para cada tabela que se queira particionar e o intervalo de particionamento – diário ou mensal.


CREATE TRIGGER partition_trg BEFORE INSERT ON history           FOR EACH ROW EXECUTE PROCEDURE trg_partition('day');
CREATE TRIGGER partition_trg BEFORE INSERT ON history_sync      FOR EACH ROW EXECUTE PROCEDURE trg_partition('day');
CREATE TRIGGER partition_trg BEFORE INSERT ON history_uint      FOR EACH ROW EXECUTE PROCEDURE trg_partition('day');
CREATE TRIGGER partition_trg BEFORE INSERT ON history_str_sync  FOR EACH ROW EXECUTE PROCEDURE trg_partition('day');
CREATE TRIGGER partition_trg BEFORE INSERT ON history_log       FOR EACH ROW EXECUTE PROCEDURE trg_partition('day');
CREATE TRIGGER partition_trg BEFORE INSERT ON trends            FOR EACH ROW EXECUTE PROCEDURE trg_partition('month');
CREATE TRIGGER partition_trg BEFORE INSERT ON trends_uint       FOR EACH ROW EXECUTE PROCEDURE trg_partition('month');

Manutenção da retenção e remoção de partições antigas

A seguinte função é utilizada para remover partições antigas e poderá ser agendada para rodar via cron.

-- Function: delete_partitions(interval, text)-- DROP FUNCTION delete_partitions(interval, text);

CREATE OR REPLACE FUNCTION delete_partitions(intervaltodelete INTERVAL, tabletype text)
RETURNS text AS
$BODY$
DECLARE
result RECORD ;
prefix text := 'partitions.';
table_timestamp TIMESTAMP;
delete_before_date DATE;
tablename text;

BEGIN
FOR result IN SELECT * FROM pg_tables WHERE schemaname = 'partitions' LOOP

table_timestamp := TO_TIMESTAMP(substring(result.tablename FROM '[0-9_]*$'), 'YYYY_MM_DD');
delete_before_date := date_trunc('day', NOW() - intervalToDelete);
tablename := result.tablename;

-- Was it called properly?
IF tabletype != 'month' AND tabletype != 'day' THEN
RAISE EXCEPTION 'Please specify "month" or "day" instead of %', tabletype;
END IF;

--Check whether the table name has a day (YYYY_MM_DD) or month (YYYY_MM) format
IF LENGTH(substring(result.tablename FROM '[0-9_]*$')) = 10 AND tabletype = 'month' THEN
--This is a daily partition YYYY_MM_DD
-- RAISE NOTICE 'Skipping table % when trying to delete "%" partitions (%)', result.tablename, tabletype, length(substring(result.tablename from '[0-9_]*$'));
CONTINUE;
ELSIF LENGTH(substring(result.tablename FROM '[0-9_]*$')) = 7 AND tabletype = 'day' THEN
--this is a monthly partition
--RAISE NOTICE 'Skipping table % when trying to delete "%" partitions (%)', result.tablename, tabletype, length(substring(result.tablename from '[0-9_]*$'));
CONTINUE;
ELSE
--This is the correct table type. Go ahead and check if it needs to be deleted
--RAISE NOTICE 'Checking table %', result.tablename;
END IF;

IF table_timestamp <= delete_before_date THEN
RAISE NOTICE 'Deleting table %', quote_ident(tablename);
EXECUTE 'DROP TABLE ' || prefix || quote_ident(tablename) || ';';
END IF;
END LOOP;
RETURN 'OK';

END;

$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION delete_partitions(INTERVAL, text)
OWNER TO postgres;

Exemplos de chamada da função:


SELECT delete_partitions('7 days', 'day')
SELECT delete_partitions('11 months', 'month')

Autenticação de usuário SVN em um diretório OpenLDAP

Um pequeno guia para realizar autenticação os usuários do openldap no Subversion utilizando SASL.

Executado em um Debian GNU/Linux 6 amd64.

Pré-requisitos: subversion previamente instalado e um repositório já criado, caso precise de um guia para a instalação e criação dos repositórios no SVN recomendo este tutorial: http://longspine.com/how-to/install-apachesubversion-on-debian-lenny-and-migrate-the-repositories/

Pacotes necessários:


apt-get install db4.7-util sasl2-bin ldap-utils

Os passos a seguir são realizados levando em consideração um repositório previamente criado em /home/svn/myproject.

Vamos editar o arquivo svnserve.conf do nosso repositório (/home/svn/myproject/conf/svnserve.conf) como o mostrado a seguir:


[general]
anon-access = none
auth-access = write

realm = myproject

[sasl]
use-sasl = true

Atenção no parâmetro realm, que deve ser o nome do repositório.

Criaremos o seguinte arquivo de definições do svn para o saslauthd em /usr/lib/sasl2/svn.conf:


#/usr/lib/sasl2/svn.conf -- might be /usr/lib/sasl2/subversion.conf not sure, make both

## Password check method, default to the SASL AUTH daemon

pwcheck_method: saslauthd

## Auxiliary (propery) plugin, use ldap

auxprop_plugin: ldap

## Mechanism list, MS AD requires you to send credentials in plain text

mech_list: PLAIN LOGIN

## Not sure if this is required... but I kept it in

ldapdb_mech: PLAIN LOGIN

Agora vem a configuração do serviço saslauthd, edite o arquivo /etc/default/saslauthd e altere os seguintes parâmetros:


START=yes
MECHANISMS="ldap"

A mágica do negócio vai no próximo arquivo, que define como o SASL vai pesquisar o diretório em busca dos usuários. Antes vamos mostrar uma DIT de exemplo, e mostar como será feita a pesquisa.

dc=exemplo,dc=com
|-cn=admin
|
|-ou=people
|  |-uid=user1
|  |-uid=user2
|
|-ou=group
|  |-cn=myproject
|  |-cn=someAnotherProject

Os usuários pertencem à classe posixAccount e os grupos à classe posixGroup e como podemos ver, criamos um grupo para cada repositório do svn e atribuímos a eles os devidos usuários usando o atribudo memberUid como mostrado abaixo:

dn: cn=myproject,ou=group,dc=exemplo,dc=com
gidNumber: 2031
cn: myproject
objectClass: top
objectClass: posixGroup
memberUid: user1
memberUid: user2

Agora editaremos o principal arquivo de configuração do SASL, o /etc/saslauthd.conf, como mostrado a seguir:


## URL for the Active Directory
ldap_servers: ldap://"openldap server ip address":389

## Not sure why exactly, but yes doesnt work... so no.
ldap_use_sasl: no

## Bind DN (Distinguishing Name) of the user you want to bind to the AD
ldap_bind_dn: cn=admin,dc=exemplo,dc=com

## Password to the above user
ldap_password: openldap_password_goes_here

## Sends passwords as plain text to AD to authenticate
ldap_mech: PLAIN

## Auth Method = Bind as specified user, and search for users in the AD
ldap_auth_method: bind

## Filter for users. (user@example.com) sAMAccountName = user
ldap_filter: uid=%U
ldap_scope: sub
ldap_password_attr: userPassword
ldap_search_base: ou=people,dc=exemplo,dc=com

## Group Filter
ldap_group_match_method: filter
ldap_group_search_base: ou=group,dc=exemplo,dc=com
ldap_group_filter: (&(objectClass=posixGroup)(cn=%r)(memberUid=%u))

O SASL ira fazer a query definida na diretiva ldap_group_filter substituindo a variável %r pelo nome do realm, isto é,  o nome do repositório definido no arquivo svnserve.conf e a variável %u pelo nome de usuário passado no login.

Assim para realizar a autenticação um usuário deve passar seu login e senha corretos, e pertencer ao grupo ldap do repositório.

Apos isso iniciamos o saslauthd e reiniciamos o subversion:


service saslauthd start
service svn restart

Podemos acompanhar as consultas com um tail no arquivo /var/log/auth.log.
Outra dica é por o saslauthd em modo debug com o comando:


saslauthd -a ldap -d

Referências:
http://notesfromchechu.com/blog/subversionopenldap/