About me

Меня зовут Павел Дерендяев, я руководитель Java-разработчиков в Альфа-Банке.

Свою карьеру в IT я начинал как системный администратор и сетевой инженер в различных телеком-компаниях. Основными языками программирования были Perl и Bash (и PHP!). В 2010 году я полностью забросил Perl/PHP и переключился на Java-разработку. В основном, писал около-биллинговые решения для тех же телекомов. Я все еще люблю настраивать сети, возиться с инфраструктурой и писать скрипты на Bash или Python.

Конкретно в Альфе-банке я занимался разработкой микросервисов для корпоративного Интернет-банка и, естественно, плотно занимался инфраструктурой нашего Mesos/Marathon-кластера.

В конце 2017 года в моей жизни случился очередной перелом. IT-блок банка трансформировался в матричную структуру, дабы разработчики не были разбросаны по разным отделам, а находились в одном центре компетенции. Цель - погружение всех в одно коммьюнити, обмен знаниями и опытом, следование общей технической стратегии и, вообще, уменьшение степени велосипедостроения.

Как руководитель Центра компетенции Java я, в основном, занимаюсь наймом разработчиков, их дальнейшим развитием, участвую в развитии общего технического слоя Java в Банке. Также занимаюсь организацией коммьюнити и продвижением HR-бренда Альфа-Банка.

Моя цель - не внедрить бесплатную раздачу печенек и бананов и не развесить гамаки рядом с каждым рабочим местом. Я хочу, чтобы разработчики занимались интересными задачами, которые их драйвят и приносят реальную ценность банку и его сотням тысяч клиентов. Не секрет, что к крутому стеку технологий у нас прилагаются сложная банковская архитектура, большие нагрузки, работа 24/7, требования безопасности и командная ответственность. Помочь разработчику не утонуть во всем этом, получать кайф от работы и “нормально делать, чтобы нормально было” - это и есть моя основная задача.

Кого я ищу в свою команду:

  • Если ты хочешь быть “в тренде” в мире Java.
  • Если ты хочешь создавать конечные решения - not only code, но и инфраструктура.
  • Если ты хочешь развивать технологии private cloud.
  • Если ты хочешь работать в команде с ведущими экспертами в области Java и DevOps.
  • Если ты хочешь стартовать новые проекты и принимать архитектурные решения.

Backend stories 4.0 - митап в Альфа-Банке

9 августа прошел Java-митап Backend stories 4.0 в Альфа-банке.

Презентации доступны на страничке с отчетом о событии.

Прямая ссылка на запись трансляции.

Мой доклад - Отказоустойчивость в большом Интернете

Backend stories 3.0 - митап в Альфа-Банке

29 марта прошел Java-митап Backend stories 3.0 в Альфа-банке.

Материалы доступны на страничке с отчетом о событии.

Прямая ссылка на запись трансляции.

Backend stories 2.0 - митап в Альфа-Банке

04 октября прошел Java-митап Backend stories 2.0 в Альфа-банке.

Все материалы доступны на страничке с отчетом о событии.

Прямая ссылка на запись трансляции.

Kafka tips & tricks

Список консьюмер-групп

docker run wurstmeister/kafka /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server kafka:9092 --list

Информация по консьюмер-группе

docker run wurstmeister/kafka /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server kafka:9092 --group id1 --describe

Установка оффсета на начало

docker run wurstmeister/kafka /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server kafka:9092 --topic topic --group id1 --reset-offsets --to-earliest --execute

Установка оффсета на конец

docker run wurstmeister/kafka /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server kafka:9092 --topic topic --group id1 --reset-offsets --to-latest --execute

Установка оффсета на дату-время

docker run wurstmeister/kafka /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server kafka:9092 --topic topic --group id1 --reset-offsets --to-datetime "2017-12-22T00:00:00.000" --execute

Установка оффсета на дату-время для партишенов 0, 1 (одно время для всех партишенов)

docker run wurstmeister/kafka /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server kafka:9092 --topic topic:0,1 --group id1 --reset-offsets --to-datetime "2017-12-22T00:00:00.000" --execute

Установка оффсета на дату-время для партишенов 0, 1 (разное время для партишенов)

docker run dddpaul/kafka-rewind --servers=kafka:9092 --group-id=id1 --topic=topic -o 0=2017-12-01 -o 1=2018-01-01

Создать топик

docker exec -it wurstmeister/kafka sh -c "JMX_PORT=10001 /opt/kafka/bin/kafka-topics.sh --create --topic topic --replication-factor 1 --partitions 1 --zookeeper zookeeper:2181"

Накидать сообщений

docker exec -it wurstmeister/kafka sh -c "JMX_PORT=10001 /opt/kafka/bin/kafka-verifiable-producer.sh --topic topic --max-messages 200000 --broker-list localhost:9092"

Консольный консьюмер

docker exec -it wurstmeister/kafka sh -c "JMX_PORT=10001 /opt/kafka/bin/kafka-console-consumer.sh --topic topic --bootstrap-server host:9092"
docker run -it wurstmeister/kafka -c "JMX_PORT=10001 /opt/kafka/bin/kafka-console-consumer.sh --topic topic --bootstrap-server host:9092"
docker run --entrypoint=/opt/kafka/bin/kafka-console-consumer.sh wurstmeister/kafka --topic topic --bootstrap-server host:9092

Python консьюмер

#!/usr/bin/env python
from kafka import KafkaConsumer
consumer = KafkaConsumer(bootstrap_servers='kafka1:9092',
                         group_id=None,
                         auto_offset_reset='earliest')
consumer.subscribe(['rsyslog_apps'])
for msg in consumer:
    print msg

Kafkacat консьюмер

docker run -it confluentinc/cp-kafkacat kafkacat -b host:9092 -t topic -o beginning -v

Установка retention на топик

docker exec -it kafka /opt/kafka/bin/kafka-configs.sh --zookeeper host:2181 --entity-type topics --entity-name topic --describe
docker exec -it kafka /opt/kafka/bin/kafka-topics.sh --zookeeper host:2181 --topic topic --describe
docker exec -it kafka /opt/kafka/bin/kafka-topics.sh --zookeeper host:2181 --topic topic --alter --config retention.ms=1000

Backend stories meetup в Альфа-Банке

21 июня прошел Java-митап Backend stories в Альфа-банке.

Доклады:

Также есть запись трансляции.

MySQL to PostgreSQL migration

Install migration Ruby gem and run it:

gem install mysql2psql
mysql2psql

Update database credentials in generated mysql2psql.yml file:

mysql:
  database: redmine
  hostname: localhost
  port: 3306
  username: redmine
  password: xxxxxxx
  encoding: utf8

destination:
 # if file is given, output goes to file, else postgres
 file: /tmp/redmine-pg.sql
 postgres:
  hostname: localhost
  port: 5432
  username: mysql2psql
  password:
  database: mysql2psql_test

Run command again:

mysql2psql

Links:

Træfik on Docker Swarm mode cluster

Træfɪk is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease. Since 1.1.0-rc1 it supports Docker Swarm mode as backend. It means that Træfɪk will automatically create proxying frontends which will be binded to corresponding Docker Swarm services.

This post is based on Docker Swarm (mode) cluster example.

Assuming we have Docker Swarm mode cluster already, we will need to create an overlay network:

docker network create --driver=overlay traefik-net

Backends are the simple emilevauge/whoami services:

docker service create --name test1 --label traefik.port=80 --network traefik-net emilevauge/whoami
docker service create --name test2 --label traefik.port=80 --network traefik-net emilevauge/whoami

Træfɪk itself may be ran in rich variety of configurations.

1. HTTP only proxy

docker service create \
--name traefik \
--constraint=node.role==manager \
--publish 80:80 --publish 8080:8080 \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
--network traefik-net \
traefik:v1.1.0-rc3 \
--docker \
--docker.swarmmode \
--docker.domain=example.org \
--docker.watch \
--logLevel=DEBUG \
--web

Remarks:

2. HTTPS proxy with Let’s Encrypt certificate and HTTP to HTTPS redirection

docker service create \
--name traefik \
--constraint=node.role==manager \
--publish 80:80 --publish 443:443 --publish 8080:8080 \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock,readonly \
--mount type=bind,source=/var/tmp,target=/etc/traefik/acme \
--network traefik-net \
traefik:v1.1.0-rc3 \
--entryPoints='Name:http Address::80 Redirect.EntryPoint:https' \
--entryPoints='Name:https Address::443 TLS' \
--defaultEntryPoints=http,https \
--acme.entryPoint=https \
--acme.email=owner@example.org \
--acme.storage=/etc/traefik/acme/acme.json \
--acme.domains=example.org \
--acme.onHostRule=true \
--docker \
--docker.swarmmode \
--docker.domain=example.org \
--docker.watch \
--web

3. HTTPS-only proxy with Let’s Encrypt certificate

docker service create \
--name traefik \
--constraint=node.role==manager \
--publish 443:443 --publish 8080:8080 \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock,readonly \
--mount type=bind,source=/var/tmp,target=/etc/traefik/acme \
--network traefik-net \
traefik:v1.1.0-rc3 \
--entryPoints='Name:https Address::443 TLS' \
--defaultEntryPoints=https \
--acme.entryPoint=https \
--acme.email=owner@example.org \
--acme.storage=/etc/traefik/acme/acme.json \
--acme.domains=example.org \
--acme.onHostRule=true \
--docker \
--docker.swarmmode \
--docker.domain=example.org \
--docker.watch \
--logLevel=DEBUG \
--web

4. HTTPS-only proxy with Let’s Encrypt certificate and HTTPS web UI

docker service create \
--name traefik \
--constraint=node.role==manager \
--publish 443:443 --publish 8443:8443 \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock,readonly \
--mount type=bind,source=/etc/pki/realms/domain,target=/etc/traefik/tls,readonly \
--mount type=bind,source=/var/tmp,target=/etc/traefik/acme \
--network traefik-net \
traefik:v1.1.0-rc3 \
--entryPoints='Name:https Address::443 TLS:/etc/traefik/tls/default.crt,/etc/traefik/tls/default.key' \
--defaultEntryPoints=https \
--acme.entryPoint=https \
--acme.email=owner@example.org \
--acme.storage=/etc/traefik/acme/acme.json \
--acme.domains=example.org \
--acme.onHostRule=true \
--docker \
--docker.swarmmode \
--docker.domain=example.org \
--docker.watch \
--logLevel=DEBUG \
--web.address=:8443 \
--web.certfile=/etc/traefik/tls/default.crt \
--web.keyfile=/etc/traefik/tls/default.key

5. For debugging purposes you can run Træfɪk without Docker

traefik -d \
--entryPoints='Name:http Address::8080 Redirect.EntryPoint:https' \
--entryPoints='Name:https Address::8443 TLS' \
--defaultEntryPoints=http,https \
--acme.entryPoint=https \
--acme.email=owner@example.org \
--acme.storage=acme.json \
--acme.domains=example.org
--logLevel=DEBUG \
--web

Links:

Docker registry on Centos 7

1. Create logical volumes for direct-lvm production mode

Assume that we have 40 GByte block device named as /dev/sdb with one full-size Linux partition on it.

Official Device Mapper storage driver guide recommends to use thin pools now. Use these commands to create thin-provisioned logical volumes:

pvcreate /dev/sdb1                 # Create physical volume
vgcreate docker /dev/sdb1          # Create volume group and add this physical volume to it
# Create logical volumes
lvcreate --wipesignatures y -n data docker -l 40%VG
lvcreate --wipesignatures y -n registry docker -l 40%VG
lvcreate --wipesignatures y -n metadata docker -l 2%VG
# Convert data volume to thin pool's data volume
lvconvert -y --zero n -c 512K --thinpool docker/data --poolmetadata docker/metadata
# Set thin pool autoextend features
cat > /etc/lvm/profile/docker-data.profile
activation {
        thin_pool_autoextend_threshold = 80
        thin_pool_autoextend_percent = 20
}
lvchange --metadataprofile docker-data docker/data
# Check thin pool volume (must be monitored) 
lvs -o+seg_monitor
  LV       VG     Attr       LSize   Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert Monitor
  root     centos -wi-ao---- 117,19g
  swap     centos -wi-ao----   1,95g
  data     docker twi-a-t---  16,00g             0,00   0,01                             monitored
  registry docker -wi-a-----  16,00g

Or if you do not trust thin pools use more traditional (but deprecated in Docker) way:

pvcreate /dev/sdb1                 # Create physical volume
vgcreate docker /dev/sdb1          # Create volume group and add this physical volume to it
lvcreate -L 2G -n metadata docker  # Create logical volume for Docker metadata
lvcreate -L 15G -n data docker     # Create logical volume for Docker data (layers, containers etc)
lvcreate -L 15G -n registry docker # Create logical volume for Docker Registry data

Mount volume for Docker registry:

mkfs.xfs /dev/docker/registry
echo "/dev/docker/registry /var/lib/docker-registry    xfs     defaults        1 3" >> /etc/fstab 
mount -a

Check:

lsblk
NAME                             MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda                                8:0    0   120G  0 disk
├─sda1                             8:1    0   876M  0 part /boot
└─sda2                             8:2    0 119,1G  0 part
  ├─centos-swap                  253:0    0     2G  0 lvm  [SWAP]
  └─centos-root                  253:1    0 117,2G  0 lvm  /
sdb                                8:16   0    40G  0 disk
└─sdb1                             8:17   0    40G  0 part
  ├─docker-metadata              253:2    0     2G  0 lvm
  │ └─docker-253:1-23762136-pool 253:5    0    15G  0 dm
  ├─docker-data                  253:3    0    15G  0 lvm
  │ └─docker-253:1-23762136-pool 253:5    0    15G  0 dm
  └─docker-registry              253:4    0    15G  0 lvm  /var/lib/docker-registry

2. Configure Docker daemon

Create systemd drop-in file:

mkdir -p /etc/systemd/system/docker.service.d
cat > /etc/systemd/system/docker.service.d/env.conf 
[Service]
EnvironmentFile=-/etc/sysconfig/docker
ExecStart=
ExecStart=/usr/bin/dockerd $OPTIONS $DOCKER_NETWORK_OPTIONS $DOCKER_STORAGE_OPTIONS

Specify Docker configuration:

cat > /etc/sysconfig/docker 
OPTIONS='--iptables=false'
DOCKER_NETWORK_OPTIONS=''
DOCKER_STORAGE_OPTIONS='--storage-driver=devicemapper --storage-opt dm.datadev=/dev/docker/data --storage-opt dm.metadatadev=/dev/docker/metadata'

Check:

systemctl daemon-reload
systemctl show docker | grep EnvironmentFile
EnvironmentFile=/etc/sysconfig/docker (ignore_errors=yes)

And run:

systemctl enable docker
systemctl restart docker

Check again:

docker info | grep data
 Data file: /dev/docker/data
 Metadata file: /dev/docker/metadata
 Metadata Space Used: 639 kB
 Metadata Space Total: 2.147 GB
 Metadata Space Available: 2.147 GB

3. Obtain SSL certificate from Let’s Encrypt

It’s can be done by different ways, see Let’s Encrypt with lego and Nginx for one of these.

Assume that certificate and key was obtained and stored in /etc/pki/tls/lego/certificates directory.

4. Run Docker registry container as systemd unit

Create systemd unit:

cat > /etc/systemd/system/docker-registry.service
[Unit]
Description=Docker registry container
Requires=docker.service
After=docker.service

[Service]
Restart=always
ExecStartPre=/usr/bin/docker create -p 5000:5000 -v /var/lib/docker-registry:/var/lib/registry -v /etc/pki/tls/lego/certificates:/certs -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/example.org.crt -e REGISTRY_HTTP_TLS_KEY=/certs/example.org.key --name registry registry:2
ExecStart=/usr/bin/docker start -a registry
ExecStop=/usr/bin/docker stop -t 5 registry
ExecStopPost=/usr/bin/docker rm registry

[Install]
WantedBy=multi-user.target

5. Permit access to Docker registry only from trusted networks

firewall-cmd --zone=trusted --add-port=5000/tcp --permanent
firewall-cmd --zone=trusted --add-source=192.168.1.0/24 --permanent
firewall-cmd --reload

Since Docker daemon was launched with --iptables=false option, Docker registry port may be accessed from trusted networks only.

Links:

Let's Encrypt with lego and Nginx

xenolf/lego it’s a feature-rich Let’s Encrypt client and ACME library written in Go.

1. Prepare Nginx server

server {
    listen 80 default;
    server_name example.org www.example.org;

    location /.well-known/acme-challenge {
        proxy_pass http://127.0.0.1:81;
        proxy_set_header Host $host;
    }

    # Other directives
}

2. Update ca-certificates for CentOS 5 (optional)

Let’s Encrypt CA certificate is not included into root CA bundle of old Linux distributions like RHEL/Centos 5. You have to replace this bundle manually with fresh one from cURL website:

cp /etc/pki/tls/certs/ca-bundle.crt /etc/pki/tls/certs/ca-bundle.crt.bak
wget -O /etc/pki/tls/certs/ca-bundle.crt http://curl.haxx.se/ca/cacert.pem

3. Order the certificate from Let’s Encrypt

lego -d example.org -d www.example.org -m cert-owner@example.org -a --path=/etc/pki/tls/lego --http=:81 run

4. Update Nginx server

server {
    listen 80 default;
    server_name example.org www.example.org;

    location /.well-known/acme-challenge {
        proxy_pass http://127.0.0.1:81;
        proxy_set_header Host $host;
    }

    # Other directives
}

server {
    listen 443 ssl;
    server_name example.org www.example.org;

    ssl_certificate /etc/pki/tls/lego/certificates/example.org.crt;
    ssl_certificate_key /etc/pki/tls/lego/certificates/example.org.key;

    location /.well-known/acme-challenge {
        proxy_pass http://127.0.0.1:444;
        proxy_set_header Host $host;
    }

    # Other directives
}

5. Renew certificate every 2 month at 01:30 of first day of the month

Add to crontab:

30 01 01 */2 * /usr/local/bin/lego -d example.org -d www.example.org -m cert-owner@example.org -a --path=/etc/pki/tls/lego --http=:81 --tls=:444 renew && /usr/sbin/nginx -s reload

Links: