SlideShare una empresa de Scribd logo
1 de 197
Descargar para leer sin conexión
Everything as a Code
Непривычно о привычном
@aatarasoff
@aatarasoff
@aatarasoff
Everything as a Code
Непривычно о привычном
No warranty guarantee
You think you are creator
5
6
… but matrix has you
7
Выйти за пределы...
8
...поняв, что всё есть код
9
Как вы представляете себе код?
10
@Configuration
@EnableConfigurationProperties(OneServerProperties.class)
public class OneServerConfiguration implements ApplicationContextAware {
ApplicationContext applicationContext;
@Autowired
OneServerProperties properties;
@Bean
public HttpServer httpServer() throws IOException {
HttpServer httpServer = new RelativePathHttpServer();
for (String beanName : context.getBeans(HttpController.class)) {
httpServer.addRequestHandlers(context.getBean(beanName));
}
return httpServer;
}
}
11
Наверное как-то так
>>,[>>,]<<[
[<<]>>>>[
<<[>+<<+>-]
>>[>+<<<<[->]>[<]>>-]
<<<[[-]>>[>+<-]>>[<<<+>>>-]]
>>[[<+>-]>>]<
]<<[>>+<<-]<<
]>>>>[.>>]
12
...или может быть так?
Что такое код?
● Коллекция инструкций
● Человекочитаемый формат
○ plain text
● Может быть понят и “проигран”
○ после компиляции
○ интерпретатором
13
Да я тоже пишу код!
14
Вооружи себя
15
Настройка рабочего окружения
16
Install apps Code Checkout
Configure
workspace
First Blood
17
Install apps Code Checkout
Configure
workspace
First Blood
brew install
18
Install apps Code Checkout
Configure
workspace
First Blood
brew install
pip install
19
Install apps Code Checkout
Configure
workspace
First Blood
git
20
Install apps Code Checkout
Configure
workspace
First Blood
customize
*.properties
21
Install apps Code Checkout
Configure
workspace
First Blood
customize
*.propertiestemplate
.gradle
22
Install apps Code Checkout
Configure
workspace
First Blood
customize
*.properties
install
certificates
23
Install apps Code Checkout
Configure
workspace
First Blood
first build
24
Install apps Code Checkout
Configure
workspace
First Blood
mass build
first deploy
25
ansible-playbook -i local-inv setup.yml --tags configure
26
Интерпретатор
ansible-playbook -i local-inv setup.yml --tags configure
27
Конфигурация
ansible-playbook -i local-inv setup.yml --tags configure
28
Алгоритм
# checkout repositories from git
- include: checkout.yml
# configure your local environment
- include: configure.yml
# add useful mappings to your hosts file
- include: hosts.yml
# add gradle support
- include: gradle.yml
# clean and build all projects
- include: build.yml
# delpoy all services to dev
- include: deploy.yml
29
Алгоритм
ansible-playbook -i local-inv setup.yml --tags configure
30
Входные параметры
- name: checkout services
git:
repo: "{{ git.root }}/{{ item.name }}.git"
dest: "{{ work_dir }}/{{ item.name }}"
update: yes
with_items:
- "{{ services }}"
tags:
- services
31
Массовый checkout/pull
- name: checkout services
git:
repo: "{{ git.root }}/{{ item.name }}.git"
dest: "{{ work_dir }}/{{ item.name }}"
update: yes
with_items:
- "{{ services }}"
tags:
- services
Переменные
32
- name: checkout services
git:
//
with_items:
- "{{ services }}"
services:
- { name: one-instant-messenger , sourceDir: src }
- { name: one-discussions , sourceDir: src }
- { name: one-attachment , sourceDir: src, testDir: test, local: true }
Циклы
33
services:
- { name: one-instant-messenger, sourceDir: src }
- { name: one-discussions, sourceDir: src }
- { name: one-attachment, sourceDir: src, testDir: test }
Коллекции и структуры данных
34
- name: create gradle build file
template:
src: build.gradle.j2
dest: "{{ work_dir }}/build.gradle"
mode: 0644
- name: create gradle settings file
template:
src: settings.gradle.j2
dest: "{{ work_dir }}/settings.gradle"
mode: 0644
Шаблоны
35
{% if services is not none %}{% for service in services %}
if (project.name == '{{ service.name }}') {
{% if 'sourceDir' in service %}
main.java.srcDir '{{ service.sourceDir }}'
{% endif %}{% if 'testDir' in service %}
test.java.srcDir '{{ service.testDir }}'
{% endif %}
}
Условные операторы
36
{% if services is not none %}{% for service in services %}
if (project.name == '{{ service.name }}') {
{% if 'sourceDir' in service %}
main.java.srcDir '{{ service.sourceDir }}'
{% endif %}{% if 'testDir' in service %}
test.java.srcDir '{{ service.testDir }}'
{% endif %}
}
Опять циклы
37
{% if services is not none %}{% for service in services %}
if (project.name == '{{ service.name }}') {
{% if 'sourceDir' in service %}
main.java.srcDir '{{ service.sourceDir }}'
{% endif %}{% if 'testDir' in service %}
test.java.srcDir '{{ service.testDir }}'
{% endif %}
}
Переменные
38
- stat: path={{ username }}.key
register: certkey
- name: generate private key
shell: openssl genrsa -out {{ username }}.key -aes256 4096
when: not certkey.stat.exists
Идемпотентность
39
Install apps Code Checkout
Configure
workspace
Multiplie Use
40
Петля обратной связи
git
Что получили
● Автоконфигурация рабочего
пространства
○ повторяемая
○ немутабельная
● Можно дать скрипт новичку
● Можно и нужно пользоваться этим
каждый день
41
Код есть код
42
Искусство сборки
43
Compile code Unit tests Package Publishing
44
Compile code Unit tests Package
compiler
Publishing
45
Compile code Unit tests Package
javacresource
processing
Publishing
46
Compile code Unit tests Package
javacresources
copyingdependency
resolving
Publishing
47
Compile code Unit tests Package
junit
Publishing
48
Compile code Unit tests Package
jar
Publishing
49
Compile code Unit tests Package
jar
npm, deb, ...
Publishing
50
Compile code Unit tests Package
jar
npm, so, ...
docker image
Publishing
51
Compile code Unit tests Package
ivy
Publishing
52
Compile code Unit tests Package
maven, ivy
maven
Publishing
53
Compile code Unit tests Package Publishing
maven, ivylocal or dev
deploydocker registry
54
Системы сборки
● Ant + Ivy
● Maven
● Gradle
● Docker
● npm
● ...
55
FROM golang:1.7-alpine
MAINTAINER aatarasoff@gmail.com
VOLUME /data
WORKDIR /data
RUN apk update && 
apk upgrade && 
apk add git bash
RUN go get github.com/aatarasoff/apistress && 
go install github.com/aatarasoff/apistress
CMD [ "apistress" ]
Dockerfile. Наследование
56
FROM golang:1.7-alpine
MAINTAINER aatarasoff@gmail.com
VOLUME /data
WORKDIR /data
RUN apk update && 
apk upgrade && 
apk add git bash
RUN go get github.com/aatarasoff/apistress && 
go install github.com/aatarasoff/apistress
CMD [ "apistress" ]
Dockerfile. Инструкции
57
FROM golang:1.7-alpine
MAINTAINER aatarasoff@gmail.com
ARG maindir=/data
VOLUME $maindir
WORKDIR $maindir
RUN apk update && apk upgrade && apk add git bash
RUN go get github.com/aatarasoff/apistress && 
go install github.com/aatarasoff/apistress
CMD [ "apistress" ]
Dockerfile. Переменные
58
docker build … && docker push …
59
publishing {
publications {
mavenJava(MavenPublication) {
artifactId 'spring-one-nio-autoconfigure'
from components.java
artifact sourceJar {
classifier "sources"
}
}
}
}
Gradle. DSL
60
compile(ivyDependencies(projectDir, 'runtime'))
def ivyDependencies(ivyPath, conf) {
def dep = []
def ivyModule = new XmlParser().parse(file("${ivyPath}/ivy.xml"))
ivyModule.dependencies.dependency.each
dep.add([group: (null == it.@org ? 'ru.odnoklassniki' : it.@org),
name: it.@name,
version: it.@rev,
configuration: (it.@conf =~ /->(w+)/)[0][1]])
}
return dep
}
Gradle. Код как он есть
61
<macrodef name="docker-build-image" description="Build docker image">
<attribute name=" buildcommand"
default="build -t @{repo}/@{image}:@{tag} ."/>
<sequential>
<exec executable="docker">
<arg line=" @{buildcommand} "/>
</exec>
</sequential>
</macrodef>
И даже в Ant-е есть жизнь
62
./gradlew build test ant-docker-build-image publish
63
Фреймворки
для автоматизации
● Ant + Ivy
● Maven
● Gradle
● Docker
● npm
● ...
64
Что получили
● Сборка это код
○ XML
○ DSL
○ Groovy
○ etc
● Системы сборки не только для
сборки
65
Ликвидация багов
66
Unit tests API tests Stress tests UI tests
67
Unit tests API tests Stress tests UI tests
TDD
68
goss --vars vars.yaml validate
69
port:
tcp:5601:
listening: true
ip:
- 0.0.0.0
service:
mesosd:
enabled: true
running: true
goss.yml
70
port:
tcp:5601:
listening: true
ip:
- 0.0.0.0
service:
mesosd:
enabled: true
running: true
goss.yml
71
port:
tcp:5601:
listening: true
ip:
- 0.0.0.0
service:
mesosd:
enabled: true
running: true
goss.yml
72
Unit tests API tests Stress tests UI tests
BDD Specs
73
def "Return error code 400, if User-ID header is not presented"() {
given:
def request = post("/rent")
when:
def result = this.mvc.perform(request)
then:
result.andExpect(status().isBadRequest())
.andDo(document("rent-user-id-is-absent"))
}
Дружелюбный BDD
74
def "Return error code 400, if User-ID header is not presented"() {
given:
def request = post("/rent")
when:
def result = this.mvc.perform(request)
then:
result.andExpect( status().isBadRequest())
.andDo(document("rent-user-id-is-absent"))
}
Простые проверки
75
Unit tests API tests Stress tests UI tests
JMeter, wrk,
vegeta
76
Unit tests API tests Stress tests UI tests
JMeter
production
77
> config | run command
> echo $?
{0,1}
0 - success
1 - error
78
Экспресс-тест
{
"baseUrl": "http://host:9000/rent-service",
"tests": [
{
"rps": 10,
"duration": 5,
"target": {
"method": "GET",
"path": "/rent",
"headers": [
...
]
},
"sla": {
"latency": 1000,
"successRate": 99.999
}
},
... ]
}
config.json
79
{
"baseUrl": "http://host:9000/rent-service",
"tests": [
{
"rps": 10,
"duration": 5,
"target": {
"method": "GET",
"path": "/rent",
"headers": [
...
]
},
"sla": {
"latency": 1000,
"successRate": 99.999
}
},
... ]
}
config.json
80
{
"baseUrl": "http://host:9000/rent-service",
"tests": [
{
"rps": 10,
"duration": 5,
"target": {
"method": "GET",
"path": "/rent",
"headers": [
...
]
},
"sla": {
"latency": 1000,
"successRate": 99.999
}
},
... ]
}
config.json
81
cat config.json | docker run -i apistress -config=stdin
82
Где-то мы такое видели
https://github.com/aatarasoff/apistress
Requests [total, rate] 50, 10.20
Duration [total, attack, wait] 5.022872793s, 4.899943287s, 122.929506ms
Latencies [mean, 50, 95, 99, max] 143.772484ms, ..., 290.101831ms
Bytes In [total, mean] 4842, 96.84
Bytes Out [total, mean] 950, 19.00
Success [ratio] 100.00%
Status Codes [code:count] 200:50
Error Set:
Test#1
83
Requests [total, rate] 50, 10.20
Duration [total, attack, wait] 5.022872793s, 4.899943287s, 122.929506ms
Latencies [mean, 50, 95, 99, max] 143.772484ms, ..., 290.101831ms
Bytes In [total, mean] 4842, 96.84
Bytes Out [total, mean] 950, 19.00
Success [ratio] 100.00%
Status Codes [code:count] 200:50
Error Set:
Test#1
84
attacker := vegeta.NewAttacker()
var metrics vegeta.Metrics
for res := range attacker.Attack(targeter, rate, duration) {
metrics.Add(res)
}
metrics.Close()
if metrics.Success*100 < test.SLA.SuccessRate {
os.Exit(1)
}
if metrics.Latencies.P99() > SLA.Latency*time.Millisecond.Nanoseconds() {
os.Exit(1)
}
Немного go-кода
85
attacker := vegeta.NewAttacker()
var metrics vegeta.Metrics
for res := range attacker.Attack( targeter, rate, duration) {
metrics.Add(res)
}
metrics.Close()
if metrics.Success*100 < test.SLA.SuccessRate {
os.Exit(1)
}
if metrics.Latencies.P99() > SLA.Latency*time.Millisecond.Nanoseconds() {
os.Exit(1)
}
Майним метрики
86
attacker := vegeta.NewAttacker()
var metrics vegeta.Metrics
for res := range attacker.Attack(targeter, rate, duration) {
metrics.Add(res)
}
metrics.Close()
if metrics.Success*100 < test.SLA.SuccessRate {
os.Exit(1)
}
if metrics.Latencies.P99() > SLA.Latency*time.Millisecond.Nanoseconds() {
os.Exit(1)
}
Не слишком ли много ошибок?
87
attacker := vegeta.NewAttacker()
var metrics vegeta.Metrics
for res := range attacker.Attack(targeter, rate, duration) {
metrics.Add(res)
}
metrics.Close()
if metrics.Success*100 < test.SLA.SuccessRate {
os.Exit(1)
}
if metrics.Latencies.P99() > SLA.Latency*time.Millisecond.Nanoseconds() {
os.Exit(1)
}
Уложились ли по времени?
88
> cat config.json | docker run -i apistress -config=stdin
> echo $?
0
89
Код возврата
Requests [total, rate] 200, 10.05
Duration [total, attack, wait] 23.04784s, 19.899754743s, 3.148093811s
Latencies [mean, 50, 95, 99, max] 3.023677499s, ..., 11.832287083s
Bytes In [total, mean] 6874, 34.37
Bytes Out [total, mean] 1349, 6.75
Success [ratio] 35.50%
Status Codes [code:count] 0:129 200:71
Error Set:
Get http://host:9000/rent-service/rent: EOF
Get http://host:9000/rent-service/rent: http: server closed idle connection
...
Test#2
90
> cat config.json | docker run -i apistress -config=stdin
> echo $?
1
91
Код возврата
Unit tests API tests Stress tests UI tests
Selenium,
Selenide
92
Что получили
● Все тестовые сценарии в коде
● Можно встроить в процесс сборки
или доставки ПО
● Если хотите, то можно
генерировать отчёты для разбора
полётов
93
Эксперименты
94
Code Branches
95
if / switch
Code Branches
96
if / switch
DI
Code Branches
97
if / switch
DI
API v2
ISupplier<String, Ctx> srcsetSupplier = (ctx) -> {
if (configuration. isDoubleDensityAvatarsEnabled(user.getModel())) {
String link = imageSrc.getModel();
return linkBuilder.createSourceSetLink(link);
}
return "";
};
Очень простой эксперимент
98
Code Branches
One server or
partition
99
//by partition
app.photos.doubleDensityAvatarsEnabled: 0
Step#1
Code Branches
One server or
partition
Part of servers
or partitions
100
//by partition
app.photos.doubleDensityAvatarsEnabled: 0,4,7-9
Step#1 Step#2
Code Branches
One server or
partition
Part of servers
or partitions
All servers or
partitions
101
//by partition
app.photos.doubleDensityAvatarsEnabled: ALL
Step#1 Step#2 Step#3
//step#1
app.photos.doubleDensityAvatarsEnabled: 0
//step#2
app.photos.doubleDensityAvatarsEnabled: 0,4,7-9
//step#3
app.photos.doubleDensityAvatarsEnabled: ALL
102
Очень простой эксперимент
No one
One server or
partition
Part of servers
or partitions
All servers or
partitions
103
monitor
Step#1 Step#2 Step#3Step#0
104
Что хотим?
ansible-playbook -i {dev,test,prod}-env exp.yml --tags stepN
105
Абстракция
- name: update properties
uri:
url: "http://{{ pms_host }}/api/conf/update"
method: POST
user: "{{ username }}"
password: "{{ password }}"
force_basic_auth: yes
body:
applicationName: "{{ application_name }}"
propertyName: "{{ item.value.name }}"
propertyValue: "{{ item.value.value }}"
body_format: json
status_code: 200
headers:
Content-Type: "application/json"
with_dict: "{{ properties }}"
106
Координаты
- name: update properties
uri:
url: "http://{{ pms_host }}/api/conf/update"
method: POST
user: "{{ username }}"
password: "{{ password }}"
force_basic_auth: yes
body:
applicationName: "{{ application_name }}"
propertyName: "{{ item.value.name }}"
propertyValue: "{{ item.value.value }}"
body_format: json
status_code: 200
headers:
Content-Type: "application/json"
with_dict: "{{ properties }}"
107
Вкатываем настройки
- name: update properties
uri:
url: "http://{{ pms_host }}/api/conf/update"
method: POST
user: "{{ username }}"
password: "{{ password }}"
force_basic_auth: yes
body:
applicationName: "{{ application_name }}"
propertyName: "{{ item.value.name }}"
propertyValue: "{{ item.value.value }}"
body_format: json
status_code: 200
headers:
Content-Type: "application/json"
with_dict: "{{ properties }}"
108
Проверяем корректность
- name: update properties
uri:
url: "http://{{ pms_host }}/api/conf/update"
method: POST
user: "{{ username }}"
password: "{{ password }}"
force_basic_auth: yes
body:
applicationName: "{{ application_name }}"
propertyName: "{{ item.value.name }}"
propertyValue: "{{ item.value.value }}"
body_format: json
status_code: 200
headers:
Content-Type: "application/json"
with_dict: "{{ properties }}"
109
Step#0
- hosts: local
vars:
application_name: odnoklassniki-web
props:
doubleDensityAvatarsEnabled:
name: "app.photos.doubleDensityAvatarsEnabled"
value: ""
roles:
- { name: pms, properties: "{{ props }}" }
110
Step#0
- hosts: local
vars:
application_name: odnoklassniki-web
props:
doubleDensityAvatarsEnabled:
name: "app.photos.doubleDensityAvatarsEnabled"
value: ""
roles:
- { name: pms, properties: "{{ props }}" }
111
Step#1
- hosts: local
vars:
application_name: odnoklassniki-web
props:
doubleDensityAvatarsEnabled:
name: "app.photos.doubleDensityAvatarsEnabled"
value: "0"
roles:
- { name: pms, properties: "{{ props }}" }
112
Step#2
- hosts: local
vars:
application_name: odnoklassniki-web
props:
doubleDensityAvatarsEnabled:
name: "app.photos.doubleDensityAvatarsEnabled"
value: "0,4,7-9"
roles:
- { name: pms, properties: "{{ props }}" }
113
Step#3
- hosts: local
vars:
application_name: odnoklassniki-web
props:
doubleDensityAvatarsEnabled:
name: "app.photos.doubleDensityAvatarsEnabled"
value: "ALL"
roles:
- { name: pms, properties: "{{ props }}" }
114
exp.yml
---
- include: step0.yml
tags:
- step0
- cleanup
- include: step1.yml
tags: step1
- include: step2.yml
tags: step2
- include: step3.yml
tags:
- step3
- complete
Что получили
● Эксперименты хранятся в git-
репозитории
● Можно применить любой шаг в
любой момент времени на любой
среде
● Можно встроить в пайплайн
доставки
115
Кододокументация
116
Analyst, PM Developer Tester Docs
Word, PDF...
117
Analyst, PM Developer Tester Docs
Word, PDF... Code + Tests
118
Analyst, PM Developer Tester Docs
Word, PDF... Code + Tests Test cases
119
Analyst, PM Developer Tester Docs :(
Word, PDF... Code + Tests Test cases
120
Analyst, PM Developer Tester Docs :)
Markdown/Asciidoctor
Docs :)
121
= Hippo Rent Service
This is documentation for Open API of our hippo renting service
== Methods
=== Rent
==== Request specification
===== Headers
//тут опишем http-заголовки
===== Example
//а здесь будут примеры вызова
==== Response specification
===== Response fields
//здесь описание полей ответа
===== Example
//ну и пример того, что вообще ожидать в ответе
Вот такой документ
122
./gradlew ... asciidoc publishDocs
123
Компиляция документа
def "Rent a hippo"() {
given:
def request = post("/rent").header("User-ID", "aatarasoff")
when:
def result = this.mvc.perform(request)
then:
result.andExpect(status().isOk())
.andDo(document(
"rent-hippo",
preprocessResponse(prettyPrint()),
requestHeaders(
headerWithName("User-ID").description("User unique identifier")),
responseFields(
fieldWithPath("hippoRemain").description("Hippo remain count"),
fieldWithPath("parrot_fee").description("Fee in virtual parrots"),
fieldWithPath("ins").description("Insurance number. Yeah, we sell it"),
fieldWithPath("hash").description("Blockchain block hash"))
))
}
…и снова тесты
124
def "Rent a hippo"() {
given:
def request = post("/rent").header("User-ID", "aatarasoff")
when:
def result = this.mvc.perform(request)
then:
result.andExpect(status().isOk())
.andDo(document(
"rent-hippo",
preprocessResponse(prettyPrint()),
requestHeaders(
headerWithName("User-ID").description("User unique identifier")),
responseFields(
fieldWithPath("hippoRemain").description("Hippo remain count"),
fieldWithPath("parrot_fee").description("Fee in virtual parrots"),
fieldWithPath("ins").description("Insurance number. Yeah, we sell it"),
fieldWithPath("hash").description("Blockchain block hash"))
))
}
А не документация ли это?
125
document(
"rent-hippo",
preprocessResponse(prettyPrint()),
requestHeaders(
headerWithName("User-ID").description("User unique identifier")),
responseFields(
fieldWithPath("hippoRemain").description("Hippo remain count"),
fieldWithPath("parrot_fee").description("Fee in virtual parrots"),
fieldWithPath("ins").description("Insurance number. Yeah, we sell
it"),
fieldWithPath("hash").description("Blockchain block hash"))
)
Имя сниппета
126
document(
"rent-hippo",
preprocessResponse(prettyPrint()),
requestHeaders(
headerWithName("User-ID").description("User unique identifier")),
responseFields(
fieldWithPath("hippoRemain").description("Hippo remain count"),
fieldWithPath("parrot_fee").description("Fee in virtual parrots"),
fieldWithPath("ins").description("Insurance number. Yeah, we sell
it"),
fieldWithPath("hash").description("Blockchain block hash"))
)
Тестируем заголовки
127
document(
"rent-hippo",
preprocessResponse(prettyPrint()),
requestHeaders(
headerWithName("User-ID").description("User unique identifier")),
responseFields(
fieldWithPath("hippoRemain").description("Hippo remain count"),
fieldWithPath("parrot_fee").description("Fee in virtual parrots"),
fieldWithPath("ins").description("Insurance number. Yeah, we sell it"),
fieldWithPath("hash").description("Blockchain block hash"))
)
Тестируем поля ответа
128
generated-snippets
rent-hippo
curl-request.adoc
http-request.adoc
http-response.adoc
httpie-request.adoc
request-headers.adoc
response-fields.adoc
Получаем сниппеты
129
= Hippo Rent Service
This is documentation for Open API of our hippo renting service
== Methods
=== Rent
==== Request specification
===== Headers
include::{snippets}/rent-hippo/request-headers.adoc[]
===== Example
include::{snippets}/rent-hippo/http-request.adoc[]
==== Response specification
===== Response fields
include::{snippets}/rent-hippo/response-fields.adoc[]
===== Example
include::{snippets}/rent-hippo/http-response.adoc[]
Вставляем их в документ
130
= Hippo Rent Service
This is documentation for Open API of our hippo renting
service
== Methods
=== Rent
==== Request specification
===== Headers
include::{snippets}/rent-hippo/request-headers.adoc[]
===== Example
include::{snippets}/rent-hippo/http-request.adoc[]
==== Response specification
===== Response fields
include::{snippets}/rent-hippo/response-fields.adoc[]
===== Example
include::{snippets}/rent-hippo/http-response.adoc[]
131
132
Что получили?
● Документация как код
○ лежит в репозитории с кодом
○ встроена в цикл сборки
○ рендерится в html, pdf и т.д.
○ почти всегда актуальна
● Синергия с тестами
133
Инфракод
134
Hardware
Containers
Application
PaaS
Mesos/Kubernetes/Private cloud
135
Hardware + OS System Libs PaaS Application
136
Hardware + OS System Libs PaaS Application
Ansible
137
Hardware + OS System Libs PaaS Application
Ansible
Puppet/Chef
138
Ansible. Inventory
[datacenter]
api-server-1
api-server-2
api-server-3
[datacenter:vars]
magicvar = 42
139
Ansible. Playbook
- hosts: datacenter
roles:
- role: docker
- role: rsyslog
140
ansible-playbook -i dc1 bootstrap.yml
141
Без комментариев
Hardware + OS System Libs PaaS Application
Ansible
142
Hardware + OS System Libs PaaS Application
Ansible
Puppet/Chef
143
Hardware + OS System Libs PaaS Application
Manifest
144
Docker compose
services:
zk:
image: zookeeper
network_mode: bridge
ports:
- 2181:2181
environment:
ZK_CONFIG: tickTime=2000,initLimit=10,clientPort=2181
ZK_ID: 1
145
Docker compose
services:
zk:
image: zookeeper
network_mode: bridge
ports:
- 2181:2181
environment:
ZK_CONFIG: tickTime=2000,initLimit=10,clientPort=2181
ZK_ID: 1
146
Конфигурация сервиса
services:
zk:
image: zookeeper
network_mode: bridge
ports:
- 2181:2181
environment:
ZK_CONFIG: tickTime=2000,initLimit=10,clientPort=2181
ZK_ID: 1
147
Mesos/Marathon
{
"id": "/api/rent-service",
"cpus": 1,
"mem": 1024,
"instances": 3,
"container": {
"docker": {
"image": "rent-service:0.0.1",
"portMappings": [
{
"containerPort": 8080
}
]
}
}
}
148
curl -X POST ... http://marathon/v2/apps?force=true
149
Современный деплоймент
Конфигурация приложений
https://your_repo/rent-service-config/routes.yml
routes:
payment:
path: /payment-service/**
serviceId: payment-service
150
Ещё конфигурация
● Zookeeper
● Consul
● Vault
● configo
● ...
151
configo
152
docker run 
-e CONFIGO_SOURCE_0='{"type": "http", "format": "yaml", "url":
"https://my.server.com/common.yaml"}' 
rent-service
//внутри приложения
getEnvironmentVariable("MY_ENV_VAR")
https://github.com/bsideup/configo
configo
153
docker run 
-e CONFIGO_SOURCE_0='{"type": "http", "format": "yaml", "url":
"https://my.server.com/common.yaml"}' 
-e CONFIGO_SOURCE_1='{"type" : "consul",
"address": "consul.master:8500",
"prefix" : "common"}' 
rent-service
//внутри приложения
getEnvironmentVariable("MY_ENV_VAR")
https://github.com/bsideup/configo
Что получили?
● Инфрастуктура может быть легко
описана в виде кода
● Деплоймент и конфигурация
приложений в виде конфигов и
манифестов
154
Неубиваемый CI
155
156
Install Master
Configure
Slaves
157
Install Master
Configure
Slaves
Ansible
158
Install Master
Configure
Slaves
Ansible
Jenkins Docker Cloud plugin
<——— Хост с докером
<——— Сколько контейнеров можно запустить
159
Автоконфигурация
<clouds>
{% for group in ['build', 'test', 'production'] %}
{% for node in groups[group + '-slaves'] %}
<com.github.kostyasha.yad.DockerCloud plugin="yet-another-docker-plugin@0.1.0-rc31">
<name>{{ node }}</name>
...
<templates>
<com.github.kostyasha.yad.DockerSlaveTemplate>
<id>mycloud-template</id>
<dockerContainerLifecycle>
<image>{{ group }}-jenkins-slave</image>
...
</templates>
<connector>
<serverUrl>tcp://{{ node.hostname }}:2375</serverUrl>
<apiVersion>1.20</apiVersion>
</connector>
</com.github.kostyasha.yad.DockerCloud>
{% endfor %}
{% endfor %}
</clouds>
160
Код доставки
161
//checkout and definition stage
node('build') {
// Mark the code checkout 'stage'
stage 'Checkout'
git credentialsId: 'jenkins-git',
url: "${git_url}/${repo}.git"
// Mark build 'stage'
stage 'Build'
sh ('./gradlew clean build final')
}
//next steps
//checkout and definition stage
node('build') {
// Mark the code checkout 'stage'
stage 'Checkout'
git credentialsId: 'jenkins-git',
url: "${git_url}/${repo}.git"
// Mark build 'stage'
stage 'Build'
sh ('./gradlew clean build final')
}
//next steps
//checkout and definition stage
node('build') {
// Mark the code checkout 'stage'
stage 'Checkout'
git credentialsId: 'jenkins-git',
url: "${git_url}/${repo}.git"
// Mark build 'stage'
stage 'Build'
sh ('./gradlew clean build final')
}
//next steps
//checkout and definition stage
node('build') {
// Mark the code checkout 'stage'
stage 'Checkout'
git credentialsId: 'jenkins-git',
url: "${git_url}/${repo}.git"
// Mark build 'stage'
stage 'Build'
sh ('./gradlew clean build final')
}
//next steps
//checkout and definition stage
node('build') {
// Mark the code checkout 'stage'
stage 'Checkout'
git credentialsId: 'jenkins-git',
url: "${git_url}/${repo}.git"
// Mark build 'stage'
stage 'Build'
sh ('./gradlew clean build final')
}
//next steps
//checkout and definition stage
node('build') {
// Mark the code checkout 'stage'
stage 'Checkout'
git credentialsId: 'jenkins-git',
url: "${git_url}/${repo}.git"
// Mark build 'stage'
stage 'Build'
sh ('./gradlew clean build final')
}
//next steps
//deploy artifact to test
node('test') {
sh('ansible-galaxy install -r requirements.yml')
ansiblePlaybook(
credentialsId: 'ansible',
installation: 'ansible',
playbook: 'deploy.yml',
inventory: 'test'
)
}
//deploy artifact to test
node('test') {
sh('ansible-galaxy install -r requirements.yml')
ansiblePlaybook(
credentialsId: 'ansible',
installation: 'ansible',
playbook: 'deploy.yml',
inventory: 'test'
)
}
//deploy artifact to test
node('test') {
sh('ansible-galaxy install -r requirements.yml')
ansiblePlaybook(
credentialsId: 'ansible',
installation: 'ansible',
playbook: 'deploy.yml',
inventory: 'test'
)
}
//deploy artifact to test
node('test') {
sh('ansible-galaxy install -r requirements.yml')
ansiblePlaybook(
credentialsId: 'ansible',
installation: 'ansible',
playbook: 'deploy.yml',
inventory: 'test'
)
}
//deploy artifact to test
node('test') {
sh('ansible-galaxy install -r requirements.yml')
ansiblePlaybook(
credentialsId: 'ansible',
installation: 'ansible',
playbook: 'deploy.yml',
inventory: 'test'
)
}
jiraComment (
issueKey: issue_id,
body: "Artifact has been deployed"
)
node('build') {
def repos = fetchRepos(project)
for (repo in repos) {
build(repo)
}
}
def fetchRepos(String project) {
def url = new URL("https://repo/projects/${project}/repos?limit=1000")
def conn = url.openConnection()
conn.setRequestMethod("GET")
def responseCode = conn.getResponseCode()
final slurper = new groovy.json.JsonSlurper()
def repos = slurper.parse(conn.getInputStream()).values
for (repo in repos) {
if (repo.slug.contains('one-'))
result << repo.slug
}
return result
}
node('build') {
def repos = fetchRepos(project)
for (repo in repos) {
build(repo)
}
}
def fetchRepos(String project) {
def url = new URL("https://repo/projects/${project}/repos?limit=1000")
def conn = url.openConnection()
conn.setRequestMethod("GET")
def responseCode = conn.getResponseCode()
final slurper = new groovy.json.JsonSlurper()
def repos = slurper.parse(conn.getInputStream()).values
for (repo in repos) {
if (repo.slug.contains('one-'))
result << repo.slug
}
return result
}
node('build') {
def repos = fetchRepos(project)
for (repo in repos) {
build(repo)
}
}
def fetchRepos(String project) {
def url = new URL("https://repo/projects/${project}/repos?limit=1000")
def conn = url.openConnection()
conn.setRequestMethod("GET")
def responseCode = conn.getResponseCode()
final slurper = new groovy.json.JsonSlurper()
def repos = slurper.parse(conn.getInputStream()).values
for (repo in repos) {
if (repo.slug.contains('one-'))
result << repo.slug
}
return result
}
Микросервисы
178
179
Install Master
Configure
Slaves
Create
meta job
Ansible
180
Install Master
Configure
Slaves
Create
meta job
Ansible
cURL
181
Install Master
Configure
Slaves
Create
meta job
Create
pipelines
jobs.each { job ->
pipelineJob("${basePath}/${job}") {
//define SCM
definition {
cps {
script(readFileFromWorkspace('pipeline-template.groovy'))
sandbox()
}
}
}
}
JobDSL plugin
182
jobs.each { job ->
pipelineJob("${basePath}/${job}") {
//define SCM
definition {
cps {
script(readFileFromWorkspace('pipeline-template.groovy'))
sandbox()
}
}
}
}
JobDSL plugin
183
jobs.each { job ->
pipelineJob("${basePath}/${job}") {
//define SCM
definition {
cps {
script(readFileFromWorkspace('pipeline-template.groovy'))
sandbox()
}
}
}
}
JobDSL plugin
184
185
Install Master
Configure
Slaves
Create
meta job
Create
pipelines
git
ansible-playbook -i jenkins-for-my-team jenkins.yml
186
Это последний раз
Что получили?
● Пайплайн как код
● Неубиваемый CI
○ без бэкапов
○ всё хранится как код
○ разворачивается за X минут
187
Development Testing Deployment
Post
Deployment
188
> ./gradlew build test
Development Testing Deployment
Post
Deployment
189
> ./gradlew integrationTest publishDocs
Development Testing Deployment
Post
Deployment
190
> ansible-playbook -i env deploy.yml
Development Testing Deployment
Post
Deployment
191
> ansible-playbook -i prod exp.yml
Development Testing Deployment
Post
Deployment
192
Delivery Pipeline
193
Позитивные выводы
● Почти любой процесс можно
формализовать, представить в
виде кода и автоматизировать
● Мы все пишем код, хотя можем
думать, что это не так
● Рано или поздно всё превращается
в код
194
Trade-offs
● Необходимы как разовые
“капиталовложения”, так и
постоянные затраты ресурсов
● Могут потребоваться изменения в
архитектуре
● Требует дисциплины и более
высокой квалификации
специалистов
195
Спасибо, что выбрали красную
@aatarasoff
@aatarasoff
@aatarasoff
QA

Más contenido relacionado

La actualidad más candente

How to stay sane during your Vagrant journey
How to stay sane during your Vagrant journeyHow to stay sane during your Vagrant journey
How to stay sane during your Vagrant journey
Jakub Wadolowski
 

La actualidad más candente (20)

Zero Downtime Deployment with Ansible
Zero Downtime Deployment with AnsibleZero Downtime Deployment with Ansible
Zero Downtime Deployment with Ansible
 
Continuous Integration: SaaS vs Jenkins in Cloud
Continuous Integration: SaaS vs Jenkins in CloudContinuous Integration: SaaS vs Jenkins in Cloud
Continuous Integration: SaaS vs Jenkins in Cloud
 
Ansible not only for Dummies
Ansible not only for DummiesAnsible not only for Dummies
Ansible not only for Dummies
 
Infrastructure as code - Python Saati #36
Infrastructure as code - Python Saati #36Infrastructure as code - Python Saati #36
Infrastructure as code - Python Saati #36
 
Making environment for_infrastructure_as_code
Making environment for_infrastructure_as_codeMaking environment for_infrastructure_as_code
Making environment for_infrastructure_as_code
 
Docker orchestration v4
Docker orchestration v4Docker orchestration v4
Docker orchestration v4
 
(Re)discover your AEM
(Re)discover your AEM(Re)discover your AEM
(Re)discover your AEM
 
How to stay sane during your Vagrant journey
How to stay sane during your Vagrant journeyHow to stay sane during your Vagrant journey
How to stay sane during your Vagrant journey
 
Preparation study of_docker - (MOSG)
Preparation study of_docker  - (MOSG)Preparation study of_docker  - (MOSG)
Preparation study of_docker - (MOSG)
 
What Have Syscalls Done for you Lately?
What Have Syscalls Done for you Lately?What Have Syscalls Done for you Lately?
What Have Syscalls Done for you Lately?
 
Into The Box 2018 Going live with commandbox and docker
Into The Box 2018 Going live with commandbox and dockerInto The Box 2018 Going live with commandbox and docker
Into The Box 2018 Going live with commandbox and docker
 
Statyczna analiza kodu PHP
Statyczna analiza kodu PHPStatyczna analiza kodu PHP
Statyczna analiza kodu PHP
 
파이썬 개발환경 구성하기의 끝판왕 - Docker Compose
파이썬 개발환경 구성하기의 끝판왕 - Docker Compose파이썬 개발환경 구성하기의 끝판왕 - Docker Compose
파이썬 개발환경 구성하기의 끝판왕 - Docker Compose
 
開放運算&GPU技術研究班
開放運算&GPU技術研究班開放運算&GPU技術研究班
開放運算&GPU技術研究班
 
DevOps(3) : Ansible - (MOSG)
DevOps(3) : Ansible - (MOSG)DevOps(3) : Ansible - (MOSG)
DevOps(3) : Ansible - (MOSG)
 
How and Why Prometheus' New Storage Engine Pushes the Limits of Time Series D...
How and Why Prometheus' New Storage Engine Pushes the Limits of Time Series D...How and Why Prometheus' New Storage Engine Pushes the Limits of Time Series D...
How and Why Prometheus' New Storage Engine Pushes the Limits of Time Series D...
 
Python virtualenv & pip in 90 minutes
Python virtualenv & pip in 90 minutesPython virtualenv & pip in 90 minutes
Python virtualenv & pip in 90 minutes
 
kubernetes practice
kubernetes practicekubernetes practice
kubernetes practice
 
Testing Wi-Fi with OSS Tools
Testing Wi-Fi with OSS ToolsTesting Wi-Fi with OSS Tools
Testing Wi-Fi with OSS Tools
 
[오픈소스컨설팅] Linux Network Troubleshooting
[오픈소스컨설팅] Linux Network Troubleshooting[오픈소스컨설팅] Linux Network Troubleshooting
[오픈소스컨설팅] Linux Network Troubleshooting
 

Similar a Everything as a Code / Александр Тарасов (Одноклассники)

Java EE 6 CDI Integrates with Spring & JSF
Java EE 6 CDI Integrates with Spring & JSFJava EE 6 CDI Integrates with Spring & JSF
Java EE 6 CDI Integrates with Spring & JSF
Jiayun Zhou
 

Similar a Everything as a Code / Александр Тарасов (Одноклассники) (20)

Js tacktalk team dev js testing performance
Js tacktalk team dev js testing performanceJs tacktalk team dev js testing performance
Js tacktalk team dev js testing performance
 
Gradle
GradleGradle
Gradle
 
Guide to Node.js: Basic to Advanced
Guide to Node.js: Basic to AdvancedGuide to Node.js: Basic to Advanced
Guide to Node.js: Basic to Advanced
 
Excelian hyperledger walkthrough-feb17
Excelian hyperledger walkthrough-feb17Excelian hyperledger walkthrough-feb17
Excelian hyperledger walkthrough-feb17
 
OWASP ZAP Workshop for QA Testers
OWASP ZAP Workshop for QA TestersOWASP ZAP Workshop for QA Testers
OWASP ZAP Workshop for QA Testers
 
Madrid JAM limitaciones - dificultades
Madrid JAM limitaciones - dificultadesMadrid JAM limitaciones - dificultades
Madrid JAM limitaciones - dificultades
 
Inria Tech Talk : Comment améliorer la qualité de vos logiciels avec STAMP
Inria Tech Talk : Comment améliorer la qualité de vos logiciels avec STAMPInria Tech Talk : Comment améliorer la qualité de vos logiciels avec STAMP
Inria Tech Talk : Comment améliorer la qualité de vos logiciels avec STAMP
 
Construire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradleConstruire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradle
 
Integration tests: use the containers, Luke!
Integration tests: use the containers, Luke!Integration tests: use the containers, Luke!
Integration tests: use the containers, Luke!
 
PuppetDB: A Single Source for Storing Your Puppet Data - PUG NY
PuppetDB: A Single Source for Storing Your Puppet Data - PUG NYPuppetDB: A Single Source for Storing Your Puppet Data - PUG NY
PuppetDB: A Single Source for Storing Your Puppet Data - PUG NY
 
Our Puppet Story (GUUG FFG 2015)
Our Puppet Story (GUUG FFG 2015)Our Puppet Story (GUUG FFG 2015)
Our Puppet Story (GUUG FFG 2015)
 
Java EE 6 CDI Integrates with Spring & JSF
Java EE 6 CDI Integrates with Spring & JSFJava EE 6 CDI Integrates with Spring & JSF
Java EE 6 CDI Integrates with Spring & JSF
 
GeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleGeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassle
 
Introduction to Codeigniter
Introduction to Codeigniter Introduction to Codeigniter
Introduction to Codeigniter
 
DevOps Workflow: A Tutorial on Linux Containers
DevOps Workflow: A Tutorial on Linux ContainersDevOps Workflow: A Tutorial on Linux Containers
DevOps Workflow: A Tutorial on Linux Containers
 
Living with garbage
Living with garbageLiving with garbage
Living with garbage
 
Living With Garbage
Living With GarbageLiving With Garbage
Living With Garbage
 
What to expect from Java 9
What to expect from Java 9What to expect from Java 9
What to expect from Java 9
 
[CB20] Vulnerabilities of Machine Learning Infrastructure by Sergey Gordeychik
[CB20] Vulnerabilities of Machine Learning Infrastructure by Sergey Gordeychik[CB20] Vulnerabilities of Machine Learning Infrastructure by Sergey Gordeychik
[CB20] Vulnerabilities of Machine Learning Infrastructure by Sergey Gordeychik
 
.gradle 파일 정독해보기
.gradle 파일 정독해보기.gradle 파일 정독해보기
.gradle 파일 정독해보기
 

Más de Ontico

Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Ontico
 

Más de Ontico (20)

One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
 
Масштабируя DNS / Артем Гавриченков (Qrator Labs)
Масштабируя DNS / Артем Гавриченков (Qrator Labs)Масштабируя DNS / Артем Гавриченков (Qrator Labs)
Масштабируя DNS / Артем Гавриченков (Qrator Labs)
 
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
 
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
 
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
 
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
 
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
 
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
 
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
 
MySQL Replication — Advanced Features / Петр Зайцев (Percona)
MySQL Replication — Advanced Features / Петр Зайцев (Percona)MySQL Replication — Advanced Features / Петр Зайцев (Percona)
MySQL Replication — Advanced Features / Петр Зайцев (Percona)
 
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
 
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
 
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
 
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
 
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
 
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
 
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
 
100500 способов кэширования в Oracle Database или как достичь максимальной ск...
100500 способов кэширования в Oracle Database или как достичь максимальной ск...100500 способов кэширования в Oracle Database или как достичь максимальной ск...
100500 способов кэширования в Oracle Database или как достичь максимальной ск...
 
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
 
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
 

Último

VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
dharasingh5698
 
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort ServiceCall Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
9953056974 Low Rate Call Girls In Saket, Delhi NCR
 
Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar ≼🔝 Delhi door step de...
Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar  ≼🔝 Delhi door step de...Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar  ≼🔝 Delhi door step de...
Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar ≼🔝 Delhi door step de...
9953056974 Low Rate Call Girls In Saket, Delhi NCR
 
Call Girls In Bangalore ☎ 7737669865 🥵 Book Your One night Stand
Call Girls In Bangalore ☎ 7737669865 🥵 Book Your One night StandCall Girls In Bangalore ☎ 7737669865 🥵 Book Your One night Stand
Call Girls In Bangalore ☎ 7737669865 🥵 Book Your One night Stand
amitlee9823
 
Cara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak Hamil
Cara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak HamilCara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak Hamil
Cara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak Hamil
Cara Menggugurkan Kandungan 087776558899
 
notes on Evolution Of Analytic Scalability.ppt
notes on Evolution Of Analytic Scalability.pptnotes on Evolution Of Analytic Scalability.ppt
notes on Evolution Of Analytic Scalability.ppt
MsecMca
 

Último (20)

Unleashing the Power of the SORA AI lastest leap
Unleashing the Power of the SORA AI lastest leapUnleashing the Power of the SORA AI lastest leap
Unleashing the Power of the SORA AI lastest leap
 
Bhosari ( Call Girls ) Pune 6297143586 Hot Model With Sexy Bhabi Ready For ...
Bhosari ( Call Girls ) Pune  6297143586  Hot Model With Sexy Bhabi Ready For ...Bhosari ( Call Girls ) Pune  6297143586  Hot Model With Sexy Bhabi Ready For ...
Bhosari ( Call Girls ) Pune 6297143586 Hot Model With Sexy Bhabi Ready For ...
 
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
 
Hostel management system project report..pdf
Hostel management system project report..pdfHostel management system project report..pdf
Hostel management system project report..pdf
 
Introduction to Serverless with AWS Lambda
Introduction to Serverless with AWS LambdaIntroduction to Serverless with AWS Lambda
Introduction to Serverless with AWS Lambda
 
Water Industry Process Automation & Control Monthly - April 2024
Water Industry Process Automation & Control Monthly - April 2024Water Industry Process Automation & Control Monthly - April 2024
Water Industry Process Automation & Control Monthly - April 2024
 
Work-Permit-Receiver-in-Saudi-Aramco.pptx
Work-Permit-Receiver-in-Saudi-Aramco.pptxWork-Permit-Receiver-in-Saudi-Aramco.pptx
Work-Permit-Receiver-in-Saudi-Aramco.pptx
 
Hazard Identification (HAZID) vs. Hazard and Operability (HAZOP): A Comparati...
Hazard Identification (HAZID) vs. Hazard and Operability (HAZOP): A Comparati...Hazard Identification (HAZID) vs. Hazard and Operability (HAZOP): A Comparati...
Hazard Identification (HAZID) vs. Hazard and Operability (HAZOP): A Comparati...
 
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort ServiceCall Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
 
22-prompt engineering noted slide shown.pdf
22-prompt engineering noted slide shown.pdf22-prompt engineering noted slide shown.pdf
22-prompt engineering noted slide shown.pdf
 
Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar ≼🔝 Delhi door step de...
Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar  ≼🔝 Delhi door step de...Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar  ≼🔝 Delhi door step de...
Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar ≼🔝 Delhi door step de...
 
Block diagram reduction techniques in control systems.ppt
Block diagram reduction techniques in control systems.pptBlock diagram reduction techniques in control systems.ppt
Block diagram reduction techniques in control systems.ppt
 
Navigating Complexity: The Role of Trusted Partners and VIAS3D in Dassault Sy...
Navigating Complexity: The Role of Trusted Partners and VIAS3D in Dassault Sy...Navigating Complexity: The Role of Trusted Partners and VIAS3D in Dassault Sy...
Navigating Complexity: The Role of Trusted Partners and VIAS3D in Dassault Sy...
 
Call Girls In Bangalore ☎ 7737669865 🥵 Book Your One night Stand
Call Girls In Bangalore ☎ 7737669865 🥵 Book Your One night StandCall Girls In Bangalore ☎ 7737669865 🥵 Book Your One night Stand
Call Girls In Bangalore ☎ 7737669865 🥵 Book Your One night Stand
 
Cara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak Hamil
Cara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak HamilCara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak Hamil
Cara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak Hamil
 
DC MACHINE-Motoring and generation, Armature circuit equation
DC MACHINE-Motoring and generation, Armature circuit equationDC MACHINE-Motoring and generation, Armature circuit equation
DC MACHINE-Motoring and generation, Armature circuit equation
 
notes on Evolution Of Analytic Scalability.ppt
notes on Evolution Of Analytic Scalability.pptnotes on Evolution Of Analytic Scalability.ppt
notes on Evolution Of Analytic Scalability.ppt
 
chapter 5.pptx: drainage and irrigation engineering
chapter 5.pptx: drainage and irrigation engineeringchapter 5.pptx: drainage and irrigation engineering
chapter 5.pptx: drainage and irrigation engineering
 
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdfONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
 
University management System project report..pdf
University management System project report..pdfUniversity management System project report..pdf
University management System project report..pdf
 

Everything as a Code / Александр Тарасов (Одноклассники)