SlideShare uma empresa Scribd logo
1 de 97
Baixar para ler offline
Retour d’expérience technique
Vincent Composieux
🐦 @vcomposieux
Architecture
monolithique
Architecture
micro-services
Architecture micro-services.
Début
~Juin 2018
Go
GraphQL
Kubernetes
4
Architecture micro-services.
Début
~Juin 2018
Mon
arrivée
Catalog
Search API
Novembre 2018
CMS
SEO API
Go
GraphQL
Kubernetes
5
Architecture micro-services.
Début
~Juin 2018
Mon
arrivée
Catalog
Search API
SEO API
Mediatheque
/
Image proxy
Novembre 2018
CMS
User
History
Favory
Reco
API
Benchmarks
Derniers
tests
/
correctifs
Go
GraphQL
Kubernetes
6
Architecture micro-services.
Début
~Juin 2018
Juin 2019
MEPMon
arrivée
Catalog
Search API
SEO API
Mediatheque
/
Image proxy
Novembre 2018
CMS
User
History
Favory
Reco
API
Benchmarks
Derniers
tests
/
correctifs
Go
GraphQL
Kubernetes
7
Architecture micro-services.
Début
Février 2020
~Juin 2018
Juin 2019
MEPMon
arrivée
Catalog
Search API
SEO API
Mediatheque
/
Image proxy
Novembre 2018
CMS
User
History
Favory
Reco
API
Benchmarks
Derniers
tests
/
correctifs
Correctifs
Post-MEP
Ajout de logs,
métriques
ISP Exporter
User Downloader
Refactoring de
code
Go
GraphQL
Kubernetes
8
Architecture micro-services.
Front
mobile
Front web
Fronts
IPTV
9
Architecture micro-services.
GraphQL
Front
mobile
Front web
Fronts
IPTV
HTTP
HTTP
HTTP
10
Back-end
Architecture micro-services.
GraphQL
Kubernetes
Catalog API
Reco API
Search API
User Favory
User History
Front
mobile
Front web
Fronts
IPTV
HTTP
HTTP
HTTP
Persona API
HTTP
11
Architecture micro-services.
HTTP.gRPC.
RabbitMQ.
AWS S3.
Azure
.Storage.
ElasticSearch.
Redis.
PostgreSQL.
~20 micro-services.
~10 devs
12
● Go
○ Utilisation du langage et avantages
○ Activation de Docker buildkit
○ gRPC / Protobuf avec Go
● Performances en production
○ GraphQL : Gestion de cache (cache applicatif)
○ GraphQL : Persisted queries (cache HTTP)
○ Proxy images : retaille d’images et invalidation
● DevOps
○ Monitoring avec Prometheus et Grafana
○ Kubernetes, Kustomize et Linkerd
○ Environnement de développement local
Sommaire.
Utilisation du langage et avantages
Utilisation du langage et avantages.
● Général
○ Facile à apprendre, les développeurs montent vite en compétence
○ Éprouvé chez Google (~10 ans)
○ Très utilisé par les outils d’infrastructure (Docker, Kubernetes, Consul, …)
○ Communauté grandissante, utilisée par des acteurs français
■ Molotov TV, Teads.tv, Algolia, Heetch, Orange, Veepee, Mention, TF1 …
○ Langage bas niveau, léger et offrant d’excellente possibilités de performances
(Goroutines)
■ var job = job.New()
■ go job.Run()
○ Possibilité d’échanger des données entre plusieurs goroutines via les channels
15
Utilisation du langage et avantages.
● Goroutine
“A goroutine is a lightweight thread managed
by the Go runtime”
https://tour.golang.org/concurrency/1
16
Utilisation du langage et avantages.
● Channel
○ Partager des valeurs entre les Goroutines sans problème d’accès concurrent
○ Un channel peut recevoir n’importe quel type (int, string, struct, func, …)
UNBUFFERED.
BUFFERED.
GR2GR1
CHANNEL.
GR2GR1
CHANNEL.
GR2GR1
CHANNEL.
GR2GR1
CHANNEL.
GR2GR1
CHANNEL.
GR2GR1
CHANNEL.
c := make(chan string) c := make(chan string, 100)
17
Utilisation du langage et avantages.
● Exemple Goroutine / channel
○ Ecriture d’un spooler (file d’attente) :
■ Empilement de données dans un channel et envoi toutes les 30 secondes
■ Si le channel atteint sa capacité maximale (100), envoi des données
30s. 30s. 30s. 30s. 30s.
Flush.
Flush.
Flush.
Flush.
Flush.
Flush.
LIMITE
18
Utilisation du langage et avantages.
● Exemple Goroutine / channel
main.go
type Spooler struct {
ItemChan chan *SpoolerItem
}
type SpoolerItem struct {
ID int
}
func main() {
var itemChan = make(chan *SpoolerItem, 100)
var spooler = &Spooler{
ItemChan: itemChan,
}
go spooler.Run()
for i := 0; i <= 100000; i++ {
item := &SpoolerItem{ID: i}
spooler.Add(item)
time.Sleep(time.Duration(rand.Intn(2000 - 100)) * time.Millisecond)
}
}
main.go
func (s *Spooler) Flush() { // Do something }
// Run executes the ticker to tick and flush every 30 seconds
func (s *Spooler) Run() {
ticker := time.NewTicker(30 * time.Second)
for range ticker.C {
s.logger.Info("Ticker: flushing queue")
s.Flush()
}
}
// Add adds a new item into the spooler queue
func (s *Spooler) Add(item *SpoolerItem) {
if len(s.ItemChan) == cap(s.ItemChan) {
s.logger.Info("Max video queue reached: flushing queue")
s.Flush()
}
s.ItemChan <- item
}
19
Utilisation du langage et avantages.
● Consommation d’une Goroutine
○ Dépend uniquement de sa consommation mémoire
○ Une Goroutine ne bloquera jamais votre programme, mais peut ralentir le garbage
collector
○ Go utilise au minimum 2kb / Goroutine (100 000 Goroutines = ~200mb)
https://github.com/golang/go/blob/master/src/runtime/stack.go#L72
20
Utilisation du langage et avantages.
● Gestion des dépendances
○ Normalisée avec les Go modules (depuis Go 1.13)
○ On travaille directement dans le répertoire du projet (plus de $GOPATH)
○ Gestion de dépendance ≠ Vendoring : GOFLAGS=-mod=vendor
go.mod
module go.tf1.fr/mytf1/catalog-graphql
go 1.13
require (
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/heetch/confita v0.8.0
github.com/prometheus/client_golang v0.9.3
go.tf1.fr/platform/cms-api v1.9.1
go.tf1.fr/platform/zap-graylog v1.4.5
go.uber.org/zap v1.10.0
)
main.go
package main
import (
"go.tf1.fr/mytf1/catalog-graphql/config"
"go.uber.org/zap"
)
func main() {
var conf = config.Load()
var logger = zap.New()
// ...
}
21
Utilisation du langage et avantages.
● Gestion des dépendances
○ Deux meta “go-import” et “go-source” retournent les informations pour accéder
au repository Git
Terminal
$ curl go.uber.org/zap
<!DOCTYPE html>
<html>
<head>
<meta name="go-import" content="go.uber.org/zap git https://github.com/uber-go/zap">
<meta name="go-source" content="go.uber.org/zap https://github.com/uber-go/zap
https://github.com/uber-go/zap/tree/master{/dir} https://github.com/uber-go/zap/tree/master{/dir}/{file}#L{line}">
22
Utilisation du langage et avantages.
● Langage compilé
○ Possibilité de compiler sur toutes les architectures:
■ GOOS=linux GOARCH=amd64 go build -o app .
○ Une fois buildé, le binaire peut être exécuté directement, sans aucune dépendance
○ Images Docker légères = limite les failles de sécurité
Terminal
$ cat Dockerfile
FROM golang:alpine as builder
ENV GOFLAGS -mod=vendor
RUN go build -o /catalog-graphql ./cmd/server
FROM scratch
COPY --from=builder /catalog-graphql /
CMD ["/catalog-graphql"]
23
Activation de Docker buildkit
Activation de Docker buildkit.
● Opt-out pour le moment
○ Activation des fonctions “experimentales” Docker buildkit sur les builds
■ Réduit significativement le temps de build
○ Une variable d’environnement à ajouter lors de “docker build” et une ligne dans le
Dockerfile :
Terminal
$ cat Dockerfile
# syntax = docker/dockerfile:experimental
FROM golang:alpine as builder
$ DOCKER_BUILDKIT=1 docker build -t mon-image .
x2
25
Activation de Docker buildkit.
● Lecture améliorée du Dockerfile
○ Lit le fichier en partant de la fin pour simplifier l’arbre de décision
○ Exécute les ordres en utilisant de la concurrence si possible
FROM
RUN
RUN
FROM
RUN
RUN
RUN
FROM
RUN
FROM
RUN
FROM
RUN
RUN
RUN
FROM
RUN
RUN
LEGACY.
BUILDKIT.
26
Activation de Docker buildkit.
● Build amélioré grâce à gRPC
○ Echanges du contexte de build grandement améliorés :
■ Legacy : le client fait un .tar et envoie tout le répertoire courant
■ Buildkit : le client envoie les fichiers Dockerfile, .dockerignore et uniquement
ce qui est nécessaire
Terminal
$ DOCKER_BUILDKIT=1 docker build -t test .
[+] Building 0.1s (5/5) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 37B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
[...]
=> writing image
sha256:ba5bca3a525ac97573b2e1d3cb936ad50cf8129eedfa9 0.0s
Terminal
$ docker build -t test .
Sending build context to Docker daemon 4.315GB
[...]
Successfully built c9ec5d33e12e
27
Activation de Docker buildkit.
● Point de montage host <> build
○ Possibilité de partager :
■ un cache de build (go, npm, composer, …)
■ un cache de packages unix (apt-get install, apk add …)
■ ...
Terminal
# syntax = docker/dockerfile:experimental
FROM golang:alpine as builder
RUN --mount=type=cache,target=/var/cache/apk 
apk add ca-certificates
RUN --mount=type=cache,target=/root/.cache/go-build 
go build -o /catalog-graphql ./cmd/server
28
Activation de Docker buildkit.
● Point de montage host <> build
○ D’autres types disponibles :
■ --mount=type=[ bind | secret | ssh ]
○ Pour plus d’informations :
■ https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md
Terminal
# syntax = docker/dockerfile:experimental
FROM golang:alpine as builder
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials 
aws s3 cp s3://... …
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
RUN --mount=type=ssh ssh -q -T git@github.com
29
gRPC / Protobuf avec Go
gRPC / Protobuf avec Go.
● Qu’est-ce-que Protobuf ?
○ Implémentation de Protocol Buffers, un format de sérialisation multi-langage (Go,
Java, Javascript, PHP, …) via une description d’interface
syntax = "proto3";
package catalog;
service Catalog {
rpc GetVideos (GetVideosRequest)
returns (GetVideosResponse) {}
}
message GetVideosRequest {
int32 offset = 1;
int32 limit = 2;
}
message GetVideosResponse {
int32 total = 1;
bool has_next = 2;
int32 offset = 3;
repeated Video items = 4;
}
message Video {
string id = 1;
string stream_id = 2;
VideoType type = 3;
string title = 4;
}
enum VideoType {
REPLAY = 0;
BONUS = 1;
EXTRACT = 2;
ADVERTISEMENT = 3;
}
31
gRPC / Protobuf avec Go.
● Qu’est-ce-que gRPC ?
○ Protocole d’échange RPC (Remote Procedure Call) qui utilise Protocol Buffers par
défaut comme langage de définition
○ Basé sur HTTP/2, offre une librairie haut-niveau pour interagir entre plusieurs
langages via un même contrat d’interface
○ Gestion des erreurs, load balancing, failover, …
○ Transit du contexte de la requête
ServerClient
Appel GetVideos()
Réponse GetVideos()
Stream de données
GoJS
32
gRPC / Protobuf avec Go.
○ Il faut tout d’abord compiler un fichier qui sera le même pour le/les client(s) et le
serveur (contrat d’interface entre les deux).
○ Compilation du fichier de définition (catalog.proto) via l’outli CLI “protoc” fourni
avec Protobuf :
Terminal
$ protoc -I ./api catalog.proto --go_out=plugins=grpc:./pkg/grpc
// Génère du code Go dans le fichier : ./pkg/grpc/catalog.pb.go
33
gRPC / Protobuf avec Go.
catalog.pb.go
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: catalog.proto
package catalog
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
wrappers "github.com/golang/protobuf/ptypes/wrappers"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
...
34
gRPC / Protobuf avec Go.
Mise en place d’un controller (struct) permettant d’accueillir les méthodes gRPC
controller.go
import (
“context”
“error”
"go.tf1.fr/mytf1/catalog-api/pkg/catalog"
)
type Controller struct { ... }
func (c *Controller) GetVideos(ctx context.Context, req *catalog.GetVideosRequest) (*catalog.GetVideosResponse, error) {
videos, err := c.elasticsearchClient.GetVideos(ctx)
if err != nil {
return nil, err
}
return &catalog.GetVideosResponse(items: videos), nil
}
35
gRPC / Protobuf avec Go.
Mise en route du serveur gRPC sur le port “8080”
server.go
import (
"net"
"go.tf1.fr/mytf1/catalog-api/pkg/catalog"
"google.golang.org/grpc"
)
func main() {
var server = grpc.NewServer()
var controller = &Controller{}
catalog.RegisterCatalogServer(server, controller)
listener, _ := net.Listen("tcp", ":8080")
if err := server.Serve(listener); err != nil {
panic(err)
}
}
36
gRPC / Protobuf avec Go.
Côté client, on contacte le serveur gRPC
server.go
import (
"context"
"go.tf1.fr/mytf1/catalog-api/pkg/catalog"
"google.golang.org/grpc"
)
func main() {
conn, _ := grpc.Dial("localhost:8080", grpc.WithInsecure())
defer conn.Close()
client := catalog.NewCatalogClient(conn)
videos, err := client.GetVideos(context.Background(), &catalog.GetVideosRequest{Offset: 0, Limit: 10})
if err != nil {
panic(err)
}
// Les vidéos sont dans la variable videos
}
37
gRPC / Protobuf avec Go.
● Simple à mettre en place
○ Une nouvelle méthode RPC s’ajoute très facilement/rapidement
● Contrat d’interface clair et versioning
○ Le fichier généré “.pb.go” ainsi que le code du client Go (controller) sont
versionnés avec le code serveur
○ Si le contrat du serveur change, on tag une nouvelle release et on récupère les
clients sur les projets via la gestion de dépendance
● Excellentes performances
○ En production, en pic de charge (soirée), 99% des méthodes RPC répondent en
moins de 100ms .
38
Performances en prod
GraphQL : Gestion de cache (cache applicatif)
GraphQL : Gestion de cache (cache applicatif).
● Au début : cache mémoire GraphQL
○ Afin de cacher les réponses (contextualisées) des micro-services tiers
■ un cache mémoire dans chaque pod GraphQL
■ Inconvénient : lors d’un déploiement en production, les caches mémoires
sont vides sur tous les pods en même temps
Pod 1
Cache mémoire
Pod 2
Cache mémoire
Pod 3
gRPC Server
HIGH
40
GraphQL : Gestion de cache (cache applicatif).
● Maintenant : cache mémoire / Redis partagé
○ Afin d’éviter de trop solliciter nos briques back-end via gRPC, nous avons ajoutés
des layers de cache dans GraphQL :
■ un cache mémoire pour les éléments les plus chauds
■ un cache Redis partagé en fallback
Redis
Pod 1
Cache mémoire
Pod 2
Cache mémoire
Cache Redis Cache Redis
Pod 3
gRPC Server
LOW
41
GraphQL : Gestion de cache (cache applicatif).
● Echange de données
○ Retourner au maximum uniquement des identifiants (vidéo, programme, …)
○ Chargement des données via une couche de “data loader” : bénéfice du cache
■ Évite de faire un nouvel appel gRPC GetVideo(id) ou GetProgram(id) si la
donnée est en cache (quelques secondes)
■ Une donnée chargée pour un utilisateur sert au suivant
Data loader
Cache HITid-1
id-2
id-3
Pod 3
gRPC ServerCache HIT
Cache MISS
id-4 Cache MISS
42
Performances en prod
GraphQL : Persisted queries (cache HTTP)
GraphQL : Persisted queries (cache HTTP).
● Que sont les persisted queries ?
○ Par défaut, les requêtes GraphQL étant en POST, pas de cache HTTP possible
GraphQL
Handler GraphQL
AWS Cloudfront
Cache MISS
POST /graphql
{
query: {
getVideos(limit: 10) {
total
items {
title
}
}
}
}
x10
44
GraphQL : Persisted queries (cache HTTP).
● Que sont les persisted queries ?
○ Par défaut, les requêtes GraphQL étant en POST, pas de cache HTTP possible
○ Solution : passer sur du GET avec un identifiant de requête (checksum)
○ Gains en bande passante (cache, payloads, trafic interne)
GraphQL
Handler GraphQL
AWS Cloudfront
Cache MISS
GET /graphql?id=37hdk087d32j4S42k55d2
Elasticsearch
Cache HIT
x10
x9
x1
45
GraphQL : Persisted queries (cache HTTP).
● Comment fait-on ?
○ Les fronts build l’ensemble des requêtes GraphQL qu’ils utilisent sur leur
application et contactent une API
○ L’API stocke l’identifiant et la requête dans un Elasticsearch
Persisted query API
37hdk087d32j4S42k55d2 Elasticsearch
{
query: {
getVideos(limit: $limit) {
total
items {
title
}
}
}
}
GraphQL
HTTP Handler
37hdk087d32j4S42k55d2
{query: {getVideos(limit: $limit){total items { title }}}}
46
Performances en prod
Proxy images : Retaille d’images et invalidation
AWS Lambda
Proxy images : Retaille d’images et invalidation.
● Contexte : image proxy
○ Documents (images, videos, ...) stockés dans Amazon S3 : ~600 000 objets
○ Accès en HTTP via Cloudfront en déclenchant une fonction AWS Lambda
○ Sécurité pour éviter de demander n’importe quelle dimension d’image
Proxy image
HTTP Handler AWS
S3
//photos.tf1.fr/<width>/<height>/<slug>-<token>@<scale>x.<ext>
<slug>
Security
Resizer
//photos.tf1.fr/600/200/jean-pierre-foucault-2h6d3@1x.png
OK
OK
AWS
Cloudfront
Cache
48
Proxy images : Retaille d’images et invalidation.
https://docs.aws.amazon.com/lambda/latest/dg//limits.html
49
Proxy images : Retaille d’images et invalidation.
● Solution : Cloudfront “multi-origins”
○ La 1ère origine, bucket AWS S3, vérifie si le contenu existe sur Amazon S3
■ Si oui : renvoie les informations de l’origine S3 à Cloudfront
■ Si non : appelle la Lambda de resize puis renvoie des informations de
l’origine S3 à Cloudfront
AWS Lambda
AWS
S3
//photos.tf1.fr/<width>/<height>/<slug>-<token>@<scale>x.<ext>
//photos.tf1.fr/600/200/jean-pierre-foucault-2h6d3@1x.png
Proxy image
Resizer
1
3
AWS
Cloudfront
Cache
4
2
50
Kubernetes
Proxy images : Retaille d’images et invalidation.
● Solution retenue : pod Kubernetes
○ Plus simple à mettre en oeuvre, délais serrés avant la MEP
○ 10 pods en production pour servir tous les contenus
○ Cache de 24h dans Cloudfront et 1h dans nginx
Proxy image
HTTP Handler AWS
S3
//photos.tf1.fr/<width>/<height>/<slug>-<token>@<scale>x.<ext>
<slug>
Security
Resizer
//photos.tf1.fr/600/200/jean-pierre-foucault-2h6d3@1x.png
OK
OK
AWS
Cloudfront
Nginx
Proxy
Cache
Cache
51
Proxy images : Retaille d’images et invalidation.
● Invalidation du cache des images
○ Lors de l’upload d’une nouvelle image, invalidation des deux caches :
■ Nginx : via le plugin “proxy_cache_purge”
Kubernetes
Proxy
image
Resizer
AWS
Cloudfront
Nginx
Proxy
Cache
Cache
go.mod
proxy_cache_revalidate on;
proxy_cache_lock on;
proxy_cache_path /mnt/images_cache levels=2:2 keys_zone=cache:100m
max_size=10g inactive=1d use_temp_path=off;
location ~ /purge(/.*) {
allow 10.1.1.0/24;
deny all;
proxy_cache_purge cache $1$is_args$args;
}
$ curl https://photos.tf1.fr/purge/600/200/jean-pierre-foucault-23h4s@1x.png
52
Proxy images : Retaille d’images et invalidation.
● Invalidation du cache des images
○ Lors de l’upload d’une nouvelle image, invalidation des deux caches :
■ Cloudfront : via le SDK AWS
Kubernetes
Proxy
image
Resizer
AWS
Cloudfront
Nginx
Proxy
Cache
Cache
invalidation.go
import "github.com/aws/aws-sdk-go/service/cloudfront"
var myImagePath = “/600/200/jean-pierre-foucault-23h4s@1x.png”
var service = cloudfront.New(session)
service.CreateInvalidation(
&cloudfront.CreateInvalidationInput{
DistributionId: <cloudfront-distribution-id>,
InvalidationBatch: &cloudfront.InvalidationBatch{
CallerReference: <unique-id>
Paths: &cloudfront.Paths{Items: []*string{myImagePath}}
},
},
)
53
DevOps
Monitoring avec Prometheus et Grafana
DevOps : Monitoring avec Prometheus et Grafana.
● Prometheus
○ Récupération et stockage de métriques dans sa propre base de données
○ Fourni un langage “PromQL” pour requêter les données
● Grafana
○ Fournit une interface web permettant de requêter des sources de données (dont
Prometheus)
○ Permet de composer des dashboards à partir de graphiques
55
DevOps : Monitoring avec Prometheus et Grafana.
DevOps : Monitoring avec Prometheus et Grafana.
● et surtout … l’alerting !
○ Être informé lorsqu’une valeur passe sur ou sous un seuil
■ Slack, email, webhook, Telegram, ...
57
DevOps : Monitoring avec Prometheus et Grafana.
Requêtes par écran.
Temps de réponse jusqu’au 99ctl.
58
DevOps : Monitoring avec Prometheus et Grafana.
59
DevOps : Monitoring avec Prometheus et Grafana.
Resolvers GraphQL en erreur.
Services gRPC en erreur.
60
DevOps : Monitoring avec Prometheus et Grafana.
● Métriques : Go
○ Le client Prometheus Go expose par défaut des métriques techniques Go :
■ Temps d’exécution médian du garbage collector
■ Nombre de Go routines en cours d’exécution
■ Mémoire utilisée par le processus Go, etc ...
61
DevOps : Monitoring avec Prometheus et Grafana.
● Métriques : Cache
○ Taux d’utilisation des différents caches mémoire (videos, programmes, …)
62
DevOps : Monitoring avec Prometheus et Grafana.
● Prometheus : récupération de métriques
○ Prometheus récupère (scrape) des métriques à un interval défini
■ Exposées en HTTP
■ Possibilité de pousser des métriques via Push Gateway
○ Tous nos micro-services exposent des métriques
Prometheus
Scraping
Kubernetes
Search API
User Favorites
...
Database
Server
Grafana
Web UI
/metrics
/metrics
/metrics
63
DevOps : Monitoring avec Prometheus et Grafana.
● Exposer une métrique avec le client Go Prometheus
○ Plusieurs types possibles : Counter, Gauge, Histogram
metrics.go
package metrics
import "github.com/prometheus/client_golang/prometheus"
var cacheCollector = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "collector",
Namespace: "cache",
Help: "This represent the number of items in cache",
},
[]string{"service", "cache_key", "metric"},
)
cacheCollector.WithLabelValues("catalog", "allVideosCaches", "hit_count").Set(3)
64
DevOps : Monitoring avec Prometheus et Grafana.
● Format de lecture des métriques par Prometheus
65
DevOps : Monitoring avec Prometheus et Grafana.
● Grafana : Requêter Prometheus
○ Langage PromQL :
■ Utilisation de fonctions (sum, rate, round, sort, …)
https://prometheus.io/docs/prometheus/latest/querying/functions/
■ Filtres par labels, utilisation d’expressions régulières et variables
66
DevOps
Kubernetes, Kustomize et Linkerd
DevOps : Kubernetes, Kustomize et Linkerd.
● Qu’est-ce-que Kubernetes ?
○ Orchestrateur de conteneurs Docker, définition de ressources YAML :
■ Ingress : Réception d’une requête à destination du cluster
■ Service : Abstraction qui définit un ensemble logique de pods et une politique
permettant d’y accéder
■ Pod : Groupement d’un ou plusieurs conteneurs
■ Deployment : Décrit la façon dont les pods sont déployés (nombre de replicas,
liveness, readiness, …)
■ Config Maps : Configuration utilisée par les déploiements/pods
Ingress
Catalog GraphQL
Service
Catalog GraphQL
Mediatheque
Pod
Mediathèque
Deployment
Catalog GraphQL
Mediatheque
Catalog GraphQL
Mediatheque
Config Maps
graphql
mediath...
68
DevOps : Kubernetes, Kustomize et Linkerd.
catalog-graph-deploy.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: catalog-graphql
env: stable
name: catalog-graphql
namespace: mytf1
spec:
replicas: 1
selector:
matchLabels:
app: catalog-graphql
env: stable
template:
metadata:
labels:
app: catalog-graphql
env: stable
spec:
containers:
envFrom:
- configMapRef:
name: catalog-graphql
image: registry.tf1.fr/mytf1/catalog-graphql:1.0
ports:
- containerPort: 8001
name: monitoring-port
- containerPort: 8000
name: graphql-port
catalog-graph-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: catalog-graphql
namespace: mytf1
data:
CATALOG_GRAPHQL_CACHE: true
CATALOG_GRAPHQL_CATALOG_API_URL: catalog-api.mytf1.svc.cluster.local:80
CATALOG_GRAPHQL_CATALOG_API_TIMEOUT: "500ms"
catalog-graph-service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: catalog-graphql
name: catalog-graphql
namespace: mytf1
spec:
ports:
- name: catalog-graphql
port: 80
targetPort: 8000
- name: catalog-graphql-monitoring
port: 81
targetPort: 8001
selector:
app: catalog-graphql
69
DevOps : Kubernetes, Kustomize et Linkerd.
● Appliquer un fichier de ressource Kubernetes
○ via le CLI Kubernetes : kubectl
metrics.go
# Application de la ressource “config map” GraphQL
$ kubectl --context integration -n mytf1 -f ./mytf1/integration/catalog-graphql-configmap.yaml
configmap/catalog-graphql configured
# Application de la ressource “deployment” GraphQL
$ kubectl --context integration -n mytf1 -f ./mytf1/integration/catalog-graphql-deploy.yaml
deployment.extensions/catalog-graphql configured
# Obtention des pods actuellement créés sur le cluster
$ kubectl --context integration -n mytf1 get pods
NAME READY STATUS RESTARTS AGE
catalog-graphql-7dc4bfd449-ktwk7 1/1 Running 0 1m
70
DevOps : Kubernetes, Kustomize et Linkerd.
● Organisation des fichiers de ressources
○ Dupliqués pour chaque environnement ...
mytf1
catalog-graphql-configmap.yaml
integration
catalog-graphql-deploy.yaml
catalog-graphql-service.yaml
catalog-graphql-configmap.yaml
validation
catalog-graphql-deploy.yaml
catalog-graphql-service.yaml
...
...
preprod
catalog-graphql-configmap.yaml
Equivalents à
.90%.
71
DevOps : Kubernetes, Kustomize et Linkerd.
● Pour la moindre modification ...
○ … X fichiers à modifier, ou X = le nombre d’environnements
mytf1
catalog-graphql-configmap.yaml
integration
catalog-graphql-deploy.yaml
catalog-graphql-service.yaml
catalog-graphql-configmap.yaml
validation
catalog-graphql-deploy.yaml
catalog-graphql-service.yaml
...
...
preprod
catalog-graphql-configmap.yaml
Equivalents à
.90%.
72
DevOps : Kubernetes, Kustomize et Linkerd.
● Kustomize à la rescousse !
○ Templating et patch de la configuration de base
mytf1
catalog-graphql
base
catalog-graphql-deploy.yaml
catalog-graphql-service.yaml
catalog-graphql-ingress.yaml
kustomization.yaml
resources
overlays
integration
validation
kustomization.yaml
patches
kustomization.yaml
73
DevOps : Kubernetes, Kustomize et Linkerd.
base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mytf1
resources:
- resources/deploy.yaml
- resources/configmap.yaml
- resources/svc.yaml
base/resources/deploy.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: catalog-graphql
name: catalog-graphql
namespace: mytf1
spec:
replicas: 1
revisionHistoryLimit: 3
selector:
matchLabels:
app: catalog-graphql
containers:
envFrom:
- configMapRef:
name: catalog-graphql
image: catalog-graphql:version
● Kustomize : Exemple
○ Déclaration des configurations de base
74
DevOps : Kubernetes, Kustomize et Linkerd.
overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mytf1
images:
- name: catalog-graphql
newName: registry.tf1.fr/mytf1/catalog-graphql
newTag: v2.0.0
resources:
- ../../base/
patchesStrategicMerge:
- patches/deploy.yaml
patches:
- patch: '[{"op": "add", "path": "/spec/template/metadata/annotations/config.linkerd.io~1proxy-memory-limit", "value": "4Gi"}]'
target:
kind: Deployment
generatorOptions:
disableNameSuffixHash: true
configMapGenerator:
- behavior: merge
files:
- MID_CAT_GRAPH_PUBLIC_KEY=files/mid-cat-graph-public-key
literals:
- MID_CAT_GRAPH_PERSISTED_QUERIES_ONLY="true"
name: catalog-graphql
namespace: mytf1
75
DevOps : Kubernetes, Kustomize et Linkerd.
● Kustomize : CLI
○ Génération des fichiers GraphQL pour l’overlay “prod”
■ Déploiement via kubectl
Terminal
# Génération des fichiers
$ kustomize build mytf1/catalog-graphql/overlays/prod
apiVersion: v1
data:
MID_CAT_GRAPH_PERSISTED_QUERIES_ONLY: true
...
# Pour appliquer les changements, utiliser kubectl
$ kustomize build mytf1/catalog-graphql/overlays/prod | kubectl --context prod -n mytf1 apply -f -
configmap/catalog-graphql unchanged
deployment.extensions/catalog-graphql configured
76
DevOps : Kubernetes, Kustomize et Linkerd.
● Helm : pour aller plus loin
○ Gestion des dépendances pendant les déploiements
■ Une “chart” Helm englobe plusieurs services
○ Gestion de versions d’une collection de plusieurs micro-services
■ Une “chart” Helm est versionnée
○ Gestion de versions des changements sur la chart
○ Simplification des rollbacks
https://helm.sh/
77
DevOps : Kubernetes, Kustomize et Linkerd.
● Linkerd : Service mesh
○ Sécurité, routage et traçabilité des flux entre micro-services
■ Gestion du load balancing / autoscaling
■ Déploiement blue/green (canary releases)
■ Vision sur le routage des requêtes entre les micro-services
■ Exposition de métriques vers Prometheus
Kubernetes
Linkerd
Web UI
GraphQL
HTTP
CONTROLPLANE.
DATAPLANE.
Pools gRPC.
Catalog API
User Favorites
...
78
DevOps : Kubernetes, Kustomize et Linkerd.
● Linkerd : Installation
○ Simple, rapide, efficace
■ Installe ses outils dans un namespace dédié (linkerd) par défaut
Terminal
# Installation du CLI Linkerd
$ curl -sL https://run.linkerd.io/install | sh
# Installation de Linkerd sur le cluster Kubernetes
$ linkerd install | kubectl apply -f -
# Vérification de l’installation
$ linkerd check
$ kubectl -n linkerd get pods
NAME READY STATUS RESTARTS AGE
linkerd-controller-5f865d7998-4v8l4 3/3 Running 0 61d
linkerd-destination-797df4fc87-kxjgn 2/2 Running 0 61d
linkerd-grafana-59875c9ff-x7cm6 2/2 Running 0 61d
linkerd-identity-ff594df8-fnx79 2/2 Running 0 61d
linkerd-prometheus-64d7df6bd7-mwpfd 2/2 Running 0 20d
...
79
DevOps : Kubernetes, Kustomize et Linkerd.
● Linkerd : Utilisation
○ Annotations Kubernetes
■ Injecte automatiquement un sidecar proxy
catalog-graphql-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
...
template:
metadata:
annotations:
linkerd.io/inject: enabled
config.linkerd.io/proxy-cpu-limit: "1"
config.linkerd.io/proxy-cpu-request: 500m
config.linkerd.io/proxy-memory-limit: 1Gi
config.linkerd.io/proxy-memory-request: 128Mi
80
DevOps : Kubernetes, Kustomize et Linkerd.
81
DevOps
Environnement de développement local
Environnement de développement local.
● Besoins des développeurs
○ Après un débat avec l’équipe, voici les besoins principaux :
■ Lancer un pool d’applications en local simplement
■ Avoir du 🔥hot reloading sur ses applications locales, plus besoin d’aller
relancer le “go run ...”
■ Faire du port-forward sur Kubernetes ou simplement via SSH
■ Avoir une reconnexion automatique lorsque qu’un forward perd la
connexion
■ Utiliser des hostnames personnalisés, mappés sur des IPs privées pour avoir
plusieurs applications mappées sur un même port
■ Tout en gardant un partage simplifié de la configuration entre les
développeurs
83
Environnement de développement local.
● Solutions existantes
Port-forward Kubernetes en local
Lancer des applications (images
Docker) dans un cluster
Kubernetes
Build d’image Docker + deploy sur
Kubernetes
https://skaffold.dev/
https://www.telepresence.io/
https://kubefwd.com/
84
Environnement de développement local.
● Skaffold : Testé mais non approuvé
○ Très bon outil mais :
■ ne permet pas de forwarder des services distants en local
■ oblige d’avoir une instance Kubernetes en local
85
Environnement de développement local.
● Finalement
○ Pas de solution tranchée
■ À titre personnel, j’ai développé Monday, un outil pour gérer tous ces cas
■ Certains l’utilisent mais pas tout le monde
■ On ne veut pas imposer un outil, chacun doit rester libre de ses outils
https://github.com/eko/monday
86
Environnement de développement local.
Terminal
$ monday
87
Environnement de développement local.
YAML Configuration
Terminal
$ monday
88
Environnement de développement local.
Local applications
Microservice 1
Microservice 2
Microservice 3
YAML Configuration
Database 1
RUNNER
Monday components
Terminal
$ monday
RUNSETUP
89
Environnement de développement local.
Local applications
Microservice 1
Microservice 2
Microservice 3
YAML Configuration
Database 1
RUNNER
WATCHER
Monday components
Hot reload
Terminal
$ monday
RUNSETUP
90
Environnement de développement local.
SSH Connection
Local applications
Microservice 1
Microservice 2
Microservice 3
YAML Configuration
Kubernetes cluster
Microservice 5
Microservice 6
Database 1
FORWARDER
RUNNER
WATCHER
Monday components
Hot reload
Terminal
$ monday
PROXY
Microservice 4
Monday Proxy
RUNSETUP
REMOTE
LOCAL
PROXY
TCP Connection
Another service
TCP
Auto
reconnect
91
Environnement de développement local.
● Monday : Configuration
○ Déclaration de plusieurs “projets”
■ 1 projet = plusieurs micro-services en local et/ou forward
Terminal
$ cat ~/monday.project.yaml
projects:
- name: “MyTF1: Catalog GraphQL + Catalog API”
local:
- *catalog-graphql-local
- *catalog-api-local
forward:
- *search-api-kubernetes
- *reco-api-kubernetes
- *reco-persona-kubernetes
- *user-favorites-kubernetes
- *user-history-kubernetes
92
Environnement de développement local.
● Monday : Configuration
○ Exemple de déclaration d’une application exécutée en local
Terminal
$ cat ~/monday.local.yaml
<: &catalog-graphql-local
name: catalog-graphql
path: $GOPATH/go.tf1.fr/mytf1/catalog-graphql
watch: true
hostname: catalog-graphql.svc.local
executable: go
args:
- run
- -mod=vendor
- cmd/server/main.go
env:
- HTTP_PORT: 8000
93
Environnement de développement local.
● Monday : Configuration
○ Exemple de déclaration d’une application “forwardée” sur Kubernetes
Terminal
$ cat ~/monday.forward.yaml
<: &search-api-kubernetes
name: search-api
type: kubernetes
values:
context: *kubernetes-context
namespace: mytf1
labels:
app: search-api
hostname: search-api.svc.local
ports:
- 8080:8080
94
Environnement de développement local.
● Monday : Exécution
○ Sélection du projet à exécuter
Terminal
$ monday
Use the arrow keys to navigate: ↓ ↑ → ←
? Which project do you want to work on?:
CMS API
CMS Front
CMS GraphQL
MyTF1: Catalog GraphQL
▸ MyTF1: Catalog GraphQL + Services API
MyTF1: Catalog Indexer Job (Scope : Full)
MyTF1: Catalog Indexer Job (Scope : Programmes & Videos)
Platform: Reco API
Platform: Search API
95
Environnement de développement local.
● Monday : Exécution
○ Lancement d’un projet
Terminal
$ monday
✔ MyTF1: Catalog GraphQL + Services API
📡 Forwarding 'search-api' over kubernetes...
📡 Forwarding 'reco-api' over kubernetes...
📡 Forwarding 'reco-persona' over kubernetes...
📡 Forwarding 'user-favorites' over kubernetes...
📡 Forwarding 'user-history' over kubernetes...
⚙ Running local app ‘catalog-graphql-local’ (go.tf1.fr/mytf1/catalog-graphql)...
⚙ Running local app ‘catalog-api-local’ (go.tf1.fr/mytf1/catalog-api)...
...
96
Merci.

Mais conteúdo relacionado

Mais procurados

Oxalide Workshop #4 - Docker, des tours dans le petit bassin
Oxalide Workshop #4 - Docker, des tours dans le petit bassinOxalide Workshop #4 - Docker, des tours dans le petit bassin
Oxalide Workshop #4 - Docker, des tours dans le petit bassin
Ludovic Piot
 

Mais procurados (20)

Développer sereinement avec Node.js
Développer sereinement avec Node.jsDévelopper sereinement avec Node.js
Développer sereinement avec Node.js
 
Paris Container Day 2016 : Conteneurisation de l’usine logicielle (Retour d'e...
Paris Container Day 2016 : Conteneurisation de l’usine logicielle (Retour d'e...Paris Container Day 2016 : Conteneurisation de l’usine logicielle (Retour d'e...
Paris Container Day 2016 : Conteneurisation de l’usine logicielle (Retour d'e...
 
Introduction à SBT
Introduction à SBTIntroduction à SBT
Introduction à SBT
 
Oxalide Workshop #4 - Docker, des tours dans le petit bassin
Oxalide Workshop #4 - Docker, des tours dans le petit bassinOxalide Workshop #4 - Docker, des tours dans le petit bassin
Oxalide Workshop #4 - Docker, des tours dans le petit bassin
 
Code, ship and run
Code, ship and runCode, ship and run
Code, ship and run
 
CI, CD, pipelines, conteneurs : la cohabitation est elle possible ?
CI, CD, pipelines, conteneurs : la cohabitation est elle possible ?CI, CD, pipelines, conteneurs : la cohabitation est elle possible ?
CI, CD, pipelines, conteneurs : la cohabitation est elle possible ?
 
Introduction à Docker et utilisation en production /Digital apéro Besançon [1...
Introduction à Docker et utilisation en production /Digital apéro Besançon [1...Introduction à Docker et utilisation en production /Digital apéro Besançon [1...
Introduction à Docker et utilisation en production /Digital apéro Besançon [1...
 
Déploiement et gestion d'un site web avec Rancher
Déploiement et gestion d'un site web avec RancherDéploiement et gestion d'un site web avec Rancher
Déploiement et gestion d'un site web avec Rancher
 
Docker@linuq
Docker@linuqDocker@linuq
Docker@linuq
 
Ansible et Jenkins
Ansible et JenkinsAnsible et Jenkins
Ansible et Jenkins
 
Workshop mesos docker devoxx fr 2016
Workshop mesos docker devoxx fr 2016Workshop mesos docker devoxx fr 2016
Workshop mesos docker devoxx fr 2016
 
Automatiser l'ère post-dev
Automatiser l'ère post-devAutomatiser l'ère post-dev
Automatiser l'ère post-dev
 
Introduction à Docker et Gaudi
Introduction à Docker et GaudiIntroduction à Docker et Gaudi
Introduction à Docker et Gaudi
 
Devoxx France : Kubernetes University, Cap sur l’orchestration Docker !
Devoxx France : Kubernetes University, Cap sur l’orchestration Docker !Devoxx France : Kubernetes University, Cap sur l’orchestration Docker !
Devoxx France : Kubernetes University, Cap sur l’orchestration Docker !
 
Une caméra 📹 en DIY sur une mangeoire d'oiseau 🐦 au milieu d’un jardin 🌳 ?
Une caméra 📹 en DIY sur une mangeoire d'oiseau 🐦 au milieu d’un jardin 🌳 ?Une caméra 📹 en DIY sur une mangeoire d'oiseau 🐦 au milieu d’un jardin 🌳 ?
Une caméra 📹 en DIY sur une mangeoire d'oiseau 🐦 au milieu d’un jardin 🌳 ?
 
Be zend docker
Be zend dockerBe zend docker
Be zend docker
 
Julien Maitrehenry - Docker, ça mange quoi au printemps
Julien Maitrehenry - Docker, ça mange quoi au printempsJulien Maitrehenry - Docker, ça mange quoi au printemps
Julien Maitrehenry - Docker, ça mange quoi au printemps
 
Java - Lombok
Java - LombokJava - Lombok
Java - Lombok
 
Intro docker
Intro dockerIntro docker
Intro docker
 
Python application packaging @ MeilleursAgents
Python application packaging @ MeilleursAgentsPython application packaging @ MeilleursAgents
Python application packaging @ MeilleursAgents
 

Semelhante a Retour d'expérience technique Go, gRPC, Kubernetes

Oxalide Workshop #4 - Docker, des tours dans le petit bassin
Oxalide Workshop #4 - Docker, des tours dans le petit bassinOxalide Workshop #4 - Docker, des tours dans le petit bassin
Oxalide Workshop #4 - Docker, des tours dans le petit bassin
Oxalide
 
5390997 Support formation : Construire et administrer vos conteneurs avec Doc...
5390997 Support formation : Construire et administrer vos conteneurs avec Doc...5390997 Support formation : Construire et administrer vos conteneurs avec Doc...
5390997 Support formation : Construire et administrer vos conteneurs avec Doc...
AbdellahELMAMOUN
 
GPars et PrettyTime - Paris JUG 2011 - Guillaume Laforge
GPars et PrettyTime - Paris JUG 2011 - Guillaume LaforgeGPars et PrettyTime - Paris JUG 2011 - Guillaume Laforge
GPars et PrettyTime - Paris JUG 2011 - Guillaume Laforge
Guillaume Laforge
 

Semelhante a Retour d'expérience technique Go, gRPC, Kubernetes (20)

Pipeline Devops - Intégration continue : ansible, jenkins, docker, jmeter...
Pipeline Devops - Intégration continue : ansible, jenkins, docker, jmeter...Pipeline Devops - Intégration continue : ansible, jenkins, docker, jmeter...
Pipeline Devops - Intégration continue : ansible, jenkins, docker, jmeter...
 
Spring Boot & Containers - Do's & Don'ts
Spring Boot & Containers - Do's & Don'tsSpring Boot & Containers - Do's & Don'ts
Spring Boot & Containers - Do's & Don'ts
 
Automatisez vos tâches répétitives avec Grunt (Blend 2013)
Automatisez vos tâches répétitives avec Grunt (Blend 2013)Automatisez vos tâches répétitives avec Grunt (Blend 2013)
Automatisez vos tâches répétitives avec Grunt (Blend 2013)
 
Oxalide Workshop #4 - Docker, des tours dans le petit bassin
Oxalide Workshop #4 - Docker, des tours dans le petit bassinOxalide Workshop #4 - Docker, des tours dans le petit bassin
Oxalide Workshop #4 - Docker, des tours dans le petit bassin
 
Comment développer un serveur métier en python/C++
Comment développer un serveur métier en python/C++Comment développer un serveur métier en python/C++
Comment développer un serveur métier en python/C++
 
Tp2: Installation d'une couche d’abstraction entre un robot physique et ros
Tp2: Installation d'une couche d’abstraction entre un robot physique et rosTp2: Installation d'une couche d’abstraction entre un robot physique et ros
Tp2: Installation d'une couche d’abstraction entre un robot physique et ros
 
Petit déjeuner "Développer sur le cloud, ou comment tout construire à partir ...
Petit déjeuner "Développer sur le cloud, ou comment tout construire à partir ...Petit déjeuner "Développer sur le cloud, ou comment tout construire à partir ...
Petit déjeuner "Développer sur le cloud, ou comment tout construire à partir ...
 
Interface texte plein écran en Go avec TView
Interface texte plein écran en Go avec TViewInterface texte plein écran en Go avec TView
Interface texte plein écran en Go avec TView
 
PHP Composer : Pourquoi ? Comment ? Et plus ...
PHP Composer : Pourquoi ? Comment ? Et plus ...PHP Composer : Pourquoi ? Comment ? Et plus ...
PHP Composer : Pourquoi ? Comment ? Et plus ...
 
5390997 Support formation : Construire et administrer vos conteneurs avec Doc...
5390997 Support formation : Construire et administrer vos conteneurs avec Doc...5390997 Support formation : Construire et administrer vos conteneurs avec Doc...
5390997 Support formation : Construire et administrer vos conteneurs avec Doc...
 
Big Data Viz (and much more!) with Apache Zeppelin
Big Data Viz (and much more!) with Apache ZeppelinBig Data Viz (and much more!) with Apache Zeppelin
Big Data Viz (and much more!) with Apache Zeppelin
 
Apache flink - prise en main rapide
Apache flink - prise en main rapideApache flink - prise en main rapide
Apache flink - prise en main rapide
 
Node.js, le pavé dans la mare
Node.js, le pavé dans la mareNode.js, le pavé dans la mare
Node.js, le pavé dans la mare
 
GPars et PrettyTime - Paris JUG 2011 - Guillaume Laforge
GPars et PrettyTime - Paris JUG 2011 - Guillaume LaforgeGPars et PrettyTime - Paris JUG 2011 - Guillaume Laforge
GPars et PrettyTime - Paris JUG 2011 - Guillaume Laforge
 
Pourquoi versionner ses githooks.pdf
Pourquoi versionner ses githooks.pdfPourquoi versionner ses githooks.pdf
Pourquoi versionner ses githooks.pdf
 
Plugins Xcode
Plugins XcodePlugins Xcode
Plugins Xcode
 
Symfony2 - Un Framework PHP 5 Performant
Symfony2 - Un Framework PHP 5 PerformantSymfony2 - Un Framework PHP 5 Performant
Symfony2 - Un Framework PHP 5 Performant
 
Sec day cuckoo_workshop
Sec day cuckoo_workshopSec day cuckoo_workshop
Sec day cuckoo_workshop
 
La face cachee des web extensions
La face cachee des web extensionsLa face cachee des web extensions
La face cachee des web extensions
 
Jenkins
JenkinsJenkins
Jenkins
 

Retour d'expérience technique Go, gRPC, Kubernetes

  • 1. Retour d’expérience technique Vincent Composieux 🐦 @vcomposieux
  • 5. Architecture micro-services. Début ~Juin 2018 Mon arrivée Catalog Search API Novembre 2018 CMS SEO API Go GraphQL Kubernetes 5
  • 6. Architecture micro-services. Début ~Juin 2018 Mon arrivée Catalog Search API SEO API Mediatheque / Image proxy Novembre 2018 CMS User History Favory Reco API Benchmarks Derniers tests / correctifs Go GraphQL Kubernetes 6
  • 7. Architecture micro-services. Début ~Juin 2018 Juin 2019 MEPMon arrivée Catalog Search API SEO API Mediatheque / Image proxy Novembre 2018 CMS User History Favory Reco API Benchmarks Derniers tests / correctifs Go GraphQL Kubernetes 7
  • 8. Architecture micro-services. Début Février 2020 ~Juin 2018 Juin 2019 MEPMon arrivée Catalog Search API SEO API Mediatheque / Image proxy Novembre 2018 CMS User History Favory Reco API Benchmarks Derniers tests / correctifs Correctifs Post-MEP Ajout de logs, métriques ISP Exporter User Downloader Refactoring de code Go GraphQL Kubernetes 8
  • 11. Back-end Architecture micro-services. GraphQL Kubernetes Catalog API Reco API Search API User Favory User History Front mobile Front web Fronts IPTV HTTP HTTP HTTP Persona API HTTP 11
  • 13. ● Go ○ Utilisation du langage et avantages ○ Activation de Docker buildkit ○ gRPC / Protobuf avec Go ● Performances en production ○ GraphQL : Gestion de cache (cache applicatif) ○ GraphQL : Persisted queries (cache HTTP) ○ Proxy images : retaille d’images et invalidation ● DevOps ○ Monitoring avec Prometheus et Grafana ○ Kubernetes, Kustomize et Linkerd ○ Environnement de développement local Sommaire.
  • 14. Utilisation du langage et avantages
  • 15. Utilisation du langage et avantages. ● Général ○ Facile à apprendre, les développeurs montent vite en compétence ○ Éprouvé chez Google (~10 ans) ○ Très utilisé par les outils d’infrastructure (Docker, Kubernetes, Consul, …) ○ Communauté grandissante, utilisée par des acteurs français ■ Molotov TV, Teads.tv, Algolia, Heetch, Orange, Veepee, Mention, TF1 … ○ Langage bas niveau, léger et offrant d’excellente possibilités de performances (Goroutines) ■ var job = job.New() ■ go job.Run() ○ Possibilité d’échanger des données entre plusieurs goroutines via les channels 15
  • 16. Utilisation du langage et avantages. ● Goroutine “A goroutine is a lightweight thread managed by the Go runtime” https://tour.golang.org/concurrency/1 16
  • 17. Utilisation du langage et avantages. ● Channel ○ Partager des valeurs entre les Goroutines sans problème d’accès concurrent ○ Un channel peut recevoir n’importe quel type (int, string, struct, func, …) UNBUFFERED. BUFFERED. GR2GR1 CHANNEL. GR2GR1 CHANNEL. GR2GR1 CHANNEL. GR2GR1 CHANNEL. GR2GR1 CHANNEL. GR2GR1 CHANNEL. c := make(chan string) c := make(chan string, 100) 17
  • 18. Utilisation du langage et avantages. ● Exemple Goroutine / channel ○ Ecriture d’un spooler (file d’attente) : ■ Empilement de données dans un channel et envoi toutes les 30 secondes ■ Si le channel atteint sa capacité maximale (100), envoi des données 30s. 30s. 30s. 30s. 30s. Flush. Flush. Flush. Flush. Flush. Flush. LIMITE 18
  • 19. Utilisation du langage et avantages. ● Exemple Goroutine / channel main.go type Spooler struct { ItemChan chan *SpoolerItem } type SpoolerItem struct { ID int } func main() { var itemChan = make(chan *SpoolerItem, 100) var spooler = &Spooler{ ItemChan: itemChan, } go spooler.Run() for i := 0; i <= 100000; i++ { item := &SpoolerItem{ID: i} spooler.Add(item) time.Sleep(time.Duration(rand.Intn(2000 - 100)) * time.Millisecond) } } main.go func (s *Spooler) Flush() { // Do something } // Run executes the ticker to tick and flush every 30 seconds func (s *Spooler) Run() { ticker := time.NewTicker(30 * time.Second) for range ticker.C { s.logger.Info("Ticker: flushing queue") s.Flush() } } // Add adds a new item into the spooler queue func (s *Spooler) Add(item *SpoolerItem) { if len(s.ItemChan) == cap(s.ItemChan) { s.logger.Info("Max video queue reached: flushing queue") s.Flush() } s.ItemChan <- item } 19
  • 20. Utilisation du langage et avantages. ● Consommation d’une Goroutine ○ Dépend uniquement de sa consommation mémoire ○ Une Goroutine ne bloquera jamais votre programme, mais peut ralentir le garbage collector ○ Go utilise au minimum 2kb / Goroutine (100 000 Goroutines = ~200mb) https://github.com/golang/go/blob/master/src/runtime/stack.go#L72 20
  • 21. Utilisation du langage et avantages. ● Gestion des dépendances ○ Normalisée avec les Go modules (depuis Go 1.13) ○ On travaille directement dans le répertoire du projet (plus de $GOPATH) ○ Gestion de dépendance ≠ Vendoring : GOFLAGS=-mod=vendor go.mod module go.tf1.fr/mytf1/catalog-graphql go 1.13 require ( github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/heetch/confita v0.8.0 github.com/prometheus/client_golang v0.9.3 go.tf1.fr/platform/cms-api v1.9.1 go.tf1.fr/platform/zap-graylog v1.4.5 go.uber.org/zap v1.10.0 ) main.go package main import ( "go.tf1.fr/mytf1/catalog-graphql/config" "go.uber.org/zap" ) func main() { var conf = config.Load() var logger = zap.New() // ... } 21
  • 22. Utilisation du langage et avantages. ● Gestion des dépendances ○ Deux meta “go-import” et “go-source” retournent les informations pour accéder au repository Git Terminal $ curl go.uber.org/zap <!DOCTYPE html> <html> <head> <meta name="go-import" content="go.uber.org/zap git https://github.com/uber-go/zap"> <meta name="go-source" content="go.uber.org/zap https://github.com/uber-go/zap https://github.com/uber-go/zap/tree/master{/dir} https://github.com/uber-go/zap/tree/master{/dir}/{file}#L{line}"> 22
  • 23. Utilisation du langage et avantages. ● Langage compilé ○ Possibilité de compiler sur toutes les architectures: ■ GOOS=linux GOARCH=amd64 go build -o app . ○ Une fois buildé, le binaire peut être exécuté directement, sans aucune dépendance ○ Images Docker légères = limite les failles de sécurité Terminal $ cat Dockerfile FROM golang:alpine as builder ENV GOFLAGS -mod=vendor RUN go build -o /catalog-graphql ./cmd/server FROM scratch COPY --from=builder /catalog-graphql / CMD ["/catalog-graphql"] 23
  • 25. Activation de Docker buildkit. ● Opt-out pour le moment ○ Activation des fonctions “experimentales” Docker buildkit sur les builds ■ Réduit significativement le temps de build ○ Une variable d’environnement à ajouter lors de “docker build” et une ligne dans le Dockerfile : Terminal $ cat Dockerfile # syntax = docker/dockerfile:experimental FROM golang:alpine as builder $ DOCKER_BUILDKIT=1 docker build -t mon-image . x2 25
  • 26. Activation de Docker buildkit. ● Lecture améliorée du Dockerfile ○ Lit le fichier en partant de la fin pour simplifier l’arbre de décision ○ Exécute les ordres en utilisant de la concurrence si possible FROM RUN RUN FROM RUN RUN RUN FROM RUN FROM RUN FROM RUN RUN RUN FROM RUN RUN LEGACY. BUILDKIT. 26
  • 27. Activation de Docker buildkit. ● Build amélioré grâce à gRPC ○ Echanges du contexte de build grandement améliorés : ■ Legacy : le client fait un .tar et envoie tout le répertoire courant ■ Buildkit : le client envoie les fichiers Dockerfile, .dockerignore et uniquement ce qui est nécessaire Terminal $ DOCKER_BUILDKIT=1 docker build -t test . [+] Building 0.1s (5/5) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 37B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s [...] => writing image sha256:ba5bca3a525ac97573b2e1d3cb936ad50cf8129eedfa9 0.0s Terminal $ docker build -t test . Sending build context to Docker daemon 4.315GB [...] Successfully built c9ec5d33e12e 27
  • 28. Activation de Docker buildkit. ● Point de montage host <> build ○ Possibilité de partager : ■ un cache de build (go, npm, composer, …) ■ un cache de packages unix (apt-get install, apk add …) ■ ... Terminal # syntax = docker/dockerfile:experimental FROM golang:alpine as builder RUN --mount=type=cache,target=/var/cache/apk apk add ca-certificates RUN --mount=type=cache,target=/root/.cache/go-build go build -o /catalog-graphql ./cmd/server 28
  • 29. Activation de Docker buildkit. ● Point de montage host <> build ○ D’autres types disponibles : ■ --mount=type=[ bind | secret | ssh ] ○ Pour plus d’informations : ■ https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md Terminal # syntax = docker/dockerfile:experimental FROM golang:alpine as builder RUN --mount=type=secret,id=aws,target=/root/.aws/credentials aws s3 cp s3://... … RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts RUN --mount=type=ssh ssh -q -T git@github.com 29
  • 30. gRPC / Protobuf avec Go
  • 31. gRPC / Protobuf avec Go. ● Qu’est-ce-que Protobuf ? ○ Implémentation de Protocol Buffers, un format de sérialisation multi-langage (Go, Java, Javascript, PHP, …) via une description d’interface syntax = "proto3"; package catalog; service Catalog { rpc GetVideos (GetVideosRequest) returns (GetVideosResponse) {} } message GetVideosRequest { int32 offset = 1; int32 limit = 2; } message GetVideosResponse { int32 total = 1; bool has_next = 2; int32 offset = 3; repeated Video items = 4; } message Video { string id = 1; string stream_id = 2; VideoType type = 3; string title = 4; } enum VideoType { REPLAY = 0; BONUS = 1; EXTRACT = 2; ADVERTISEMENT = 3; } 31
  • 32. gRPC / Protobuf avec Go. ● Qu’est-ce-que gRPC ? ○ Protocole d’échange RPC (Remote Procedure Call) qui utilise Protocol Buffers par défaut comme langage de définition ○ Basé sur HTTP/2, offre une librairie haut-niveau pour interagir entre plusieurs langages via un même contrat d’interface ○ Gestion des erreurs, load balancing, failover, … ○ Transit du contexte de la requête ServerClient Appel GetVideos() Réponse GetVideos() Stream de données GoJS 32
  • 33. gRPC / Protobuf avec Go. ○ Il faut tout d’abord compiler un fichier qui sera le même pour le/les client(s) et le serveur (contrat d’interface entre les deux). ○ Compilation du fichier de définition (catalog.proto) via l’outli CLI “protoc” fourni avec Protobuf : Terminal $ protoc -I ./api catalog.proto --go_out=plugins=grpc:./pkg/grpc // Génère du code Go dans le fichier : ./pkg/grpc/catalog.pb.go 33
  • 34. gRPC / Protobuf avec Go. catalog.pb.go // Code generated by protoc-gen-go. DO NOT EDIT. // source: catalog.proto package catalog import ( context "context" fmt "fmt" proto "github.com/golang/protobuf/proto" wrappers "github.com/golang/protobuf/ptypes/wrappers" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" math "math" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package ... 34
  • 35. gRPC / Protobuf avec Go. Mise en place d’un controller (struct) permettant d’accueillir les méthodes gRPC controller.go import ( “context” “error” "go.tf1.fr/mytf1/catalog-api/pkg/catalog" ) type Controller struct { ... } func (c *Controller) GetVideos(ctx context.Context, req *catalog.GetVideosRequest) (*catalog.GetVideosResponse, error) { videos, err := c.elasticsearchClient.GetVideos(ctx) if err != nil { return nil, err } return &catalog.GetVideosResponse(items: videos), nil } 35
  • 36. gRPC / Protobuf avec Go. Mise en route du serveur gRPC sur le port “8080” server.go import ( "net" "go.tf1.fr/mytf1/catalog-api/pkg/catalog" "google.golang.org/grpc" ) func main() { var server = grpc.NewServer() var controller = &Controller{} catalog.RegisterCatalogServer(server, controller) listener, _ := net.Listen("tcp", ":8080") if err := server.Serve(listener); err != nil { panic(err) } } 36
  • 37. gRPC / Protobuf avec Go. Côté client, on contacte le serveur gRPC server.go import ( "context" "go.tf1.fr/mytf1/catalog-api/pkg/catalog" "google.golang.org/grpc" ) func main() { conn, _ := grpc.Dial("localhost:8080", grpc.WithInsecure()) defer conn.Close() client := catalog.NewCatalogClient(conn) videos, err := client.GetVideos(context.Background(), &catalog.GetVideosRequest{Offset: 0, Limit: 10}) if err != nil { panic(err) } // Les vidéos sont dans la variable videos } 37
  • 38. gRPC / Protobuf avec Go. ● Simple à mettre en place ○ Une nouvelle méthode RPC s’ajoute très facilement/rapidement ● Contrat d’interface clair et versioning ○ Le fichier généré “.pb.go” ainsi que le code du client Go (controller) sont versionnés avec le code serveur ○ Si le contrat du serveur change, on tag une nouvelle release et on récupère les clients sur les projets via la gestion de dépendance ● Excellentes performances ○ En production, en pic de charge (soirée), 99% des méthodes RPC répondent en moins de 100ms . 38
  • 39. Performances en prod GraphQL : Gestion de cache (cache applicatif)
  • 40. GraphQL : Gestion de cache (cache applicatif). ● Au début : cache mémoire GraphQL ○ Afin de cacher les réponses (contextualisées) des micro-services tiers ■ un cache mémoire dans chaque pod GraphQL ■ Inconvénient : lors d’un déploiement en production, les caches mémoires sont vides sur tous les pods en même temps Pod 1 Cache mémoire Pod 2 Cache mémoire Pod 3 gRPC Server HIGH 40
  • 41. GraphQL : Gestion de cache (cache applicatif). ● Maintenant : cache mémoire / Redis partagé ○ Afin d’éviter de trop solliciter nos briques back-end via gRPC, nous avons ajoutés des layers de cache dans GraphQL : ■ un cache mémoire pour les éléments les plus chauds ■ un cache Redis partagé en fallback Redis Pod 1 Cache mémoire Pod 2 Cache mémoire Cache Redis Cache Redis Pod 3 gRPC Server LOW 41
  • 42. GraphQL : Gestion de cache (cache applicatif). ● Echange de données ○ Retourner au maximum uniquement des identifiants (vidéo, programme, …) ○ Chargement des données via une couche de “data loader” : bénéfice du cache ■ Évite de faire un nouvel appel gRPC GetVideo(id) ou GetProgram(id) si la donnée est en cache (quelques secondes) ■ Une donnée chargée pour un utilisateur sert au suivant Data loader Cache HITid-1 id-2 id-3 Pod 3 gRPC ServerCache HIT Cache MISS id-4 Cache MISS 42
  • 43. Performances en prod GraphQL : Persisted queries (cache HTTP)
  • 44. GraphQL : Persisted queries (cache HTTP). ● Que sont les persisted queries ? ○ Par défaut, les requêtes GraphQL étant en POST, pas de cache HTTP possible GraphQL Handler GraphQL AWS Cloudfront Cache MISS POST /graphql { query: { getVideos(limit: 10) { total items { title } } } } x10 44
  • 45. GraphQL : Persisted queries (cache HTTP). ● Que sont les persisted queries ? ○ Par défaut, les requêtes GraphQL étant en POST, pas de cache HTTP possible ○ Solution : passer sur du GET avec un identifiant de requête (checksum) ○ Gains en bande passante (cache, payloads, trafic interne) GraphQL Handler GraphQL AWS Cloudfront Cache MISS GET /graphql?id=37hdk087d32j4S42k55d2 Elasticsearch Cache HIT x10 x9 x1 45
  • 46. GraphQL : Persisted queries (cache HTTP). ● Comment fait-on ? ○ Les fronts build l’ensemble des requêtes GraphQL qu’ils utilisent sur leur application et contactent une API ○ L’API stocke l’identifiant et la requête dans un Elasticsearch Persisted query API 37hdk087d32j4S42k55d2 Elasticsearch { query: { getVideos(limit: $limit) { total items { title } } } } GraphQL HTTP Handler 37hdk087d32j4S42k55d2 {query: {getVideos(limit: $limit){total items { title }}}} 46
  • 47. Performances en prod Proxy images : Retaille d’images et invalidation
  • 48. AWS Lambda Proxy images : Retaille d’images et invalidation. ● Contexte : image proxy ○ Documents (images, videos, ...) stockés dans Amazon S3 : ~600 000 objets ○ Accès en HTTP via Cloudfront en déclenchant une fonction AWS Lambda ○ Sécurité pour éviter de demander n’importe quelle dimension d’image Proxy image HTTP Handler AWS S3 //photos.tf1.fr/<width>/<height>/<slug>-<token>@<scale>x.<ext> <slug> Security Resizer //photos.tf1.fr/600/200/jean-pierre-foucault-2h6d3@1x.png OK OK AWS Cloudfront Cache 48
  • 49. Proxy images : Retaille d’images et invalidation. https://docs.aws.amazon.com/lambda/latest/dg//limits.html 49
  • 50. Proxy images : Retaille d’images et invalidation. ● Solution : Cloudfront “multi-origins” ○ La 1ère origine, bucket AWS S3, vérifie si le contenu existe sur Amazon S3 ■ Si oui : renvoie les informations de l’origine S3 à Cloudfront ■ Si non : appelle la Lambda de resize puis renvoie des informations de l’origine S3 à Cloudfront AWS Lambda AWS S3 //photos.tf1.fr/<width>/<height>/<slug>-<token>@<scale>x.<ext> //photos.tf1.fr/600/200/jean-pierre-foucault-2h6d3@1x.png Proxy image Resizer 1 3 AWS Cloudfront Cache 4 2 50
  • 51. Kubernetes Proxy images : Retaille d’images et invalidation. ● Solution retenue : pod Kubernetes ○ Plus simple à mettre en oeuvre, délais serrés avant la MEP ○ 10 pods en production pour servir tous les contenus ○ Cache de 24h dans Cloudfront et 1h dans nginx Proxy image HTTP Handler AWS S3 //photos.tf1.fr/<width>/<height>/<slug>-<token>@<scale>x.<ext> <slug> Security Resizer //photos.tf1.fr/600/200/jean-pierre-foucault-2h6d3@1x.png OK OK AWS Cloudfront Nginx Proxy Cache Cache 51
  • 52. Proxy images : Retaille d’images et invalidation. ● Invalidation du cache des images ○ Lors de l’upload d’une nouvelle image, invalidation des deux caches : ■ Nginx : via le plugin “proxy_cache_purge” Kubernetes Proxy image Resizer AWS Cloudfront Nginx Proxy Cache Cache go.mod proxy_cache_revalidate on; proxy_cache_lock on; proxy_cache_path /mnt/images_cache levels=2:2 keys_zone=cache:100m max_size=10g inactive=1d use_temp_path=off; location ~ /purge(/.*) { allow 10.1.1.0/24; deny all; proxy_cache_purge cache $1$is_args$args; } $ curl https://photos.tf1.fr/purge/600/200/jean-pierre-foucault-23h4s@1x.png 52
  • 53. Proxy images : Retaille d’images et invalidation. ● Invalidation du cache des images ○ Lors de l’upload d’une nouvelle image, invalidation des deux caches : ■ Cloudfront : via le SDK AWS Kubernetes Proxy image Resizer AWS Cloudfront Nginx Proxy Cache Cache invalidation.go import "github.com/aws/aws-sdk-go/service/cloudfront" var myImagePath = “/600/200/jean-pierre-foucault-23h4s@1x.png” var service = cloudfront.New(session) service.CreateInvalidation( &cloudfront.CreateInvalidationInput{ DistributionId: <cloudfront-distribution-id>, InvalidationBatch: &cloudfront.InvalidationBatch{ CallerReference: <unique-id> Paths: &cloudfront.Paths{Items: []*string{myImagePath}} }, }, ) 53
  • 55. DevOps : Monitoring avec Prometheus et Grafana. ● Prometheus ○ Récupération et stockage de métriques dans sa propre base de données ○ Fourni un langage “PromQL” pour requêter les données ● Grafana ○ Fournit une interface web permettant de requêter des sources de données (dont Prometheus) ○ Permet de composer des dashboards à partir de graphiques 55
  • 56. DevOps : Monitoring avec Prometheus et Grafana.
  • 57. DevOps : Monitoring avec Prometheus et Grafana. ● et surtout … l’alerting ! ○ Être informé lorsqu’une valeur passe sur ou sous un seuil ■ Slack, email, webhook, Telegram, ... 57
  • 58. DevOps : Monitoring avec Prometheus et Grafana. Requêtes par écran. Temps de réponse jusqu’au 99ctl. 58
  • 59. DevOps : Monitoring avec Prometheus et Grafana. 59
  • 60. DevOps : Monitoring avec Prometheus et Grafana. Resolvers GraphQL en erreur. Services gRPC en erreur. 60
  • 61. DevOps : Monitoring avec Prometheus et Grafana. ● Métriques : Go ○ Le client Prometheus Go expose par défaut des métriques techniques Go : ■ Temps d’exécution médian du garbage collector ■ Nombre de Go routines en cours d’exécution ■ Mémoire utilisée par le processus Go, etc ... 61
  • 62. DevOps : Monitoring avec Prometheus et Grafana. ● Métriques : Cache ○ Taux d’utilisation des différents caches mémoire (videos, programmes, …) 62
  • 63. DevOps : Monitoring avec Prometheus et Grafana. ● Prometheus : récupération de métriques ○ Prometheus récupère (scrape) des métriques à un interval défini ■ Exposées en HTTP ■ Possibilité de pousser des métriques via Push Gateway ○ Tous nos micro-services exposent des métriques Prometheus Scraping Kubernetes Search API User Favorites ... Database Server Grafana Web UI /metrics /metrics /metrics 63
  • 64. DevOps : Monitoring avec Prometheus et Grafana. ● Exposer une métrique avec le client Go Prometheus ○ Plusieurs types possibles : Counter, Gauge, Histogram metrics.go package metrics import "github.com/prometheus/client_golang/prometheus" var cacheCollector = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Name: "collector", Namespace: "cache", Help: "This represent the number of items in cache", }, []string{"service", "cache_key", "metric"}, ) cacheCollector.WithLabelValues("catalog", "allVideosCaches", "hit_count").Set(3) 64
  • 65. DevOps : Monitoring avec Prometheus et Grafana. ● Format de lecture des métriques par Prometheus 65
  • 66. DevOps : Monitoring avec Prometheus et Grafana. ● Grafana : Requêter Prometheus ○ Langage PromQL : ■ Utilisation de fonctions (sum, rate, round, sort, …) https://prometheus.io/docs/prometheus/latest/querying/functions/ ■ Filtres par labels, utilisation d’expressions régulières et variables 66
  • 68. DevOps : Kubernetes, Kustomize et Linkerd. ● Qu’est-ce-que Kubernetes ? ○ Orchestrateur de conteneurs Docker, définition de ressources YAML : ■ Ingress : Réception d’une requête à destination du cluster ■ Service : Abstraction qui définit un ensemble logique de pods et une politique permettant d’y accéder ■ Pod : Groupement d’un ou plusieurs conteneurs ■ Deployment : Décrit la façon dont les pods sont déployés (nombre de replicas, liveness, readiness, …) ■ Config Maps : Configuration utilisée par les déploiements/pods Ingress Catalog GraphQL Service Catalog GraphQL Mediatheque Pod Mediathèque Deployment Catalog GraphQL Mediatheque Catalog GraphQL Mediatheque Config Maps graphql mediath... 68
  • 69. DevOps : Kubernetes, Kustomize et Linkerd. catalog-graph-deploy.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: app: catalog-graphql env: stable name: catalog-graphql namespace: mytf1 spec: replicas: 1 selector: matchLabels: app: catalog-graphql env: stable template: metadata: labels: app: catalog-graphql env: stable spec: containers: envFrom: - configMapRef: name: catalog-graphql image: registry.tf1.fr/mytf1/catalog-graphql:1.0 ports: - containerPort: 8001 name: monitoring-port - containerPort: 8000 name: graphql-port catalog-graph-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: catalog-graphql namespace: mytf1 data: CATALOG_GRAPHQL_CACHE: true CATALOG_GRAPHQL_CATALOG_API_URL: catalog-api.mytf1.svc.cluster.local:80 CATALOG_GRAPHQL_CATALOG_API_TIMEOUT: "500ms" catalog-graph-service.yaml apiVersion: v1 kind: Service metadata: labels: app: catalog-graphql name: catalog-graphql namespace: mytf1 spec: ports: - name: catalog-graphql port: 80 targetPort: 8000 - name: catalog-graphql-monitoring port: 81 targetPort: 8001 selector: app: catalog-graphql 69
  • 70. DevOps : Kubernetes, Kustomize et Linkerd. ● Appliquer un fichier de ressource Kubernetes ○ via le CLI Kubernetes : kubectl metrics.go # Application de la ressource “config map” GraphQL $ kubectl --context integration -n mytf1 -f ./mytf1/integration/catalog-graphql-configmap.yaml configmap/catalog-graphql configured # Application de la ressource “deployment” GraphQL $ kubectl --context integration -n mytf1 -f ./mytf1/integration/catalog-graphql-deploy.yaml deployment.extensions/catalog-graphql configured # Obtention des pods actuellement créés sur le cluster $ kubectl --context integration -n mytf1 get pods NAME READY STATUS RESTARTS AGE catalog-graphql-7dc4bfd449-ktwk7 1/1 Running 0 1m 70
  • 71. DevOps : Kubernetes, Kustomize et Linkerd. ● Organisation des fichiers de ressources ○ Dupliqués pour chaque environnement ... mytf1 catalog-graphql-configmap.yaml integration catalog-graphql-deploy.yaml catalog-graphql-service.yaml catalog-graphql-configmap.yaml validation catalog-graphql-deploy.yaml catalog-graphql-service.yaml ... ... preprod catalog-graphql-configmap.yaml Equivalents à .90%. 71
  • 72. DevOps : Kubernetes, Kustomize et Linkerd. ● Pour la moindre modification ... ○ … X fichiers à modifier, ou X = le nombre d’environnements mytf1 catalog-graphql-configmap.yaml integration catalog-graphql-deploy.yaml catalog-graphql-service.yaml catalog-graphql-configmap.yaml validation catalog-graphql-deploy.yaml catalog-graphql-service.yaml ... ... preprod catalog-graphql-configmap.yaml Equivalents à .90%. 72
  • 73. DevOps : Kubernetes, Kustomize et Linkerd. ● Kustomize à la rescousse ! ○ Templating et patch de la configuration de base mytf1 catalog-graphql base catalog-graphql-deploy.yaml catalog-graphql-service.yaml catalog-graphql-ingress.yaml kustomization.yaml resources overlays integration validation kustomization.yaml patches kustomization.yaml 73
  • 74. DevOps : Kubernetes, Kustomize et Linkerd. base/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: mytf1 resources: - resources/deploy.yaml - resources/configmap.yaml - resources/svc.yaml base/resources/deploy.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: app: catalog-graphql name: catalog-graphql namespace: mytf1 spec: replicas: 1 revisionHistoryLimit: 3 selector: matchLabels: app: catalog-graphql containers: envFrom: - configMapRef: name: catalog-graphql image: catalog-graphql:version ● Kustomize : Exemple ○ Déclaration des configurations de base 74
  • 75. DevOps : Kubernetes, Kustomize et Linkerd. overlays/prod/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: mytf1 images: - name: catalog-graphql newName: registry.tf1.fr/mytf1/catalog-graphql newTag: v2.0.0 resources: - ../../base/ patchesStrategicMerge: - patches/deploy.yaml patches: - patch: '[{"op": "add", "path": "/spec/template/metadata/annotations/config.linkerd.io~1proxy-memory-limit", "value": "4Gi"}]' target: kind: Deployment generatorOptions: disableNameSuffixHash: true configMapGenerator: - behavior: merge files: - MID_CAT_GRAPH_PUBLIC_KEY=files/mid-cat-graph-public-key literals: - MID_CAT_GRAPH_PERSISTED_QUERIES_ONLY="true" name: catalog-graphql namespace: mytf1 75
  • 76. DevOps : Kubernetes, Kustomize et Linkerd. ● Kustomize : CLI ○ Génération des fichiers GraphQL pour l’overlay “prod” ■ Déploiement via kubectl Terminal # Génération des fichiers $ kustomize build mytf1/catalog-graphql/overlays/prod apiVersion: v1 data: MID_CAT_GRAPH_PERSISTED_QUERIES_ONLY: true ... # Pour appliquer les changements, utiliser kubectl $ kustomize build mytf1/catalog-graphql/overlays/prod | kubectl --context prod -n mytf1 apply -f - configmap/catalog-graphql unchanged deployment.extensions/catalog-graphql configured 76
  • 77. DevOps : Kubernetes, Kustomize et Linkerd. ● Helm : pour aller plus loin ○ Gestion des dépendances pendant les déploiements ■ Une “chart” Helm englobe plusieurs services ○ Gestion de versions d’une collection de plusieurs micro-services ■ Une “chart” Helm est versionnée ○ Gestion de versions des changements sur la chart ○ Simplification des rollbacks https://helm.sh/ 77
  • 78. DevOps : Kubernetes, Kustomize et Linkerd. ● Linkerd : Service mesh ○ Sécurité, routage et traçabilité des flux entre micro-services ■ Gestion du load balancing / autoscaling ■ Déploiement blue/green (canary releases) ■ Vision sur le routage des requêtes entre les micro-services ■ Exposition de métriques vers Prometheus Kubernetes Linkerd Web UI GraphQL HTTP CONTROLPLANE. DATAPLANE. Pools gRPC. Catalog API User Favorites ... 78
  • 79. DevOps : Kubernetes, Kustomize et Linkerd. ● Linkerd : Installation ○ Simple, rapide, efficace ■ Installe ses outils dans un namespace dédié (linkerd) par défaut Terminal # Installation du CLI Linkerd $ curl -sL https://run.linkerd.io/install | sh # Installation de Linkerd sur le cluster Kubernetes $ linkerd install | kubectl apply -f - # Vérification de l’installation $ linkerd check $ kubectl -n linkerd get pods NAME READY STATUS RESTARTS AGE linkerd-controller-5f865d7998-4v8l4 3/3 Running 0 61d linkerd-destination-797df4fc87-kxjgn 2/2 Running 0 61d linkerd-grafana-59875c9ff-x7cm6 2/2 Running 0 61d linkerd-identity-ff594df8-fnx79 2/2 Running 0 61d linkerd-prometheus-64d7df6bd7-mwpfd 2/2 Running 0 20d ... 79
  • 80. DevOps : Kubernetes, Kustomize et Linkerd. ● Linkerd : Utilisation ○ Annotations Kubernetes ■ Injecte automatiquement un sidecar proxy catalog-graphql-deployment.yaml apiVersion: extensions/v1beta1 kind: Deployment ... template: metadata: annotations: linkerd.io/inject: enabled config.linkerd.io/proxy-cpu-limit: "1" config.linkerd.io/proxy-cpu-request: 500m config.linkerd.io/proxy-memory-limit: 1Gi config.linkerd.io/proxy-memory-request: 128Mi 80
  • 81. DevOps : Kubernetes, Kustomize et Linkerd. 81
  • 83. Environnement de développement local. ● Besoins des développeurs ○ Après un débat avec l’équipe, voici les besoins principaux : ■ Lancer un pool d’applications en local simplement ■ Avoir du 🔥hot reloading sur ses applications locales, plus besoin d’aller relancer le “go run ...” ■ Faire du port-forward sur Kubernetes ou simplement via SSH ■ Avoir une reconnexion automatique lorsque qu’un forward perd la connexion ■ Utiliser des hostnames personnalisés, mappés sur des IPs privées pour avoir plusieurs applications mappées sur un même port ■ Tout en gardant un partage simplifié de la configuration entre les développeurs 83
  • 84. Environnement de développement local. ● Solutions existantes Port-forward Kubernetes en local Lancer des applications (images Docker) dans un cluster Kubernetes Build d’image Docker + deploy sur Kubernetes https://skaffold.dev/ https://www.telepresence.io/ https://kubefwd.com/ 84
  • 85. Environnement de développement local. ● Skaffold : Testé mais non approuvé ○ Très bon outil mais : ■ ne permet pas de forwarder des services distants en local ■ oblige d’avoir une instance Kubernetes en local 85
  • 86. Environnement de développement local. ● Finalement ○ Pas de solution tranchée ■ À titre personnel, j’ai développé Monday, un outil pour gérer tous ces cas ■ Certains l’utilisent mais pas tout le monde ■ On ne veut pas imposer un outil, chacun doit rester libre de ses outils https://github.com/eko/monday 86
  • 87. Environnement de développement local. Terminal $ monday 87
  • 88. Environnement de développement local. YAML Configuration Terminal $ monday 88
  • 89. Environnement de développement local. Local applications Microservice 1 Microservice 2 Microservice 3 YAML Configuration Database 1 RUNNER Monday components Terminal $ monday RUNSETUP 89
  • 90. Environnement de développement local. Local applications Microservice 1 Microservice 2 Microservice 3 YAML Configuration Database 1 RUNNER WATCHER Monday components Hot reload Terminal $ monday RUNSETUP 90
  • 91. Environnement de développement local. SSH Connection Local applications Microservice 1 Microservice 2 Microservice 3 YAML Configuration Kubernetes cluster Microservice 5 Microservice 6 Database 1 FORWARDER RUNNER WATCHER Monday components Hot reload Terminal $ monday PROXY Microservice 4 Monday Proxy RUNSETUP REMOTE LOCAL PROXY TCP Connection Another service TCP Auto reconnect 91
  • 92. Environnement de développement local. ● Monday : Configuration ○ Déclaration de plusieurs “projets” ■ 1 projet = plusieurs micro-services en local et/ou forward Terminal $ cat ~/monday.project.yaml projects: - name: “MyTF1: Catalog GraphQL + Catalog API” local: - *catalog-graphql-local - *catalog-api-local forward: - *search-api-kubernetes - *reco-api-kubernetes - *reco-persona-kubernetes - *user-favorites-kubernetes - *user-history-kubernetes 92
  • 93. Environnement de développement local. ● Monday : Configuration ○ Exemple de déclaration d’une application exécutée en local Terminal $ cat ~/monday.local.yaml <: &catalog-graphql-local name: catalog-graphql path: $GOPATH/go.tf1.fr/mytf1/catalog-graphql watch: true hostname: catalog-graphql.svc.local executable: go args: - run - -mod=vendor - cmd/server/main.go env: - HTTP_PORT: 8000 93
  • 94. Environnement de développement local. ● Monday : Configuration ○ Exemple de déclaration d’une application “forwardée” sur Kubernetes Terminal $ cat ~/monday.forward.yaml <: &search-api-kubernetes name: search-api type: kubernetes values: context: *kubernetes-context namespace: mytf1 labels: app: search-api hostname: search-api.svc.local ports: - 8080:8080 94
  • 95. Environnement de développement local. ● Monday : Exécution ○ Sélection du projet à exécuter Terminal $ monday Use the arrow keys to navigate: ↓ ↑ → ← ? Which project do you want to work on?: CMS API CMS Front CMS GraphQL MyTF1: Catalog GraphQL ▸ MyTF1: Catalog GraphQL + Services API MyTF1: Catalog Indexer Job (Scope : Full) MyTF1: Catalog Indexer Job (Scope : Programmes & Videos) Platform: Reco API Platform: Search API 95
  • 96. Environnement de développement local. ● Monday : Exécution ○ Lancement d’un projet Terminal $ monday ✔ MyTF1: Catalog GraphQL + Services API 📡 Forwarding 'search-api' over kubernetes... 📡 Forwarding 'reco-api' over kubernetes... 📡 Forwarding 'reco-persona' over kubernetes... 📡 Forwarding 'user-favorites' over kubernetes... 📡 Forwarding 'user-history' over kubernetes... ⚙ Running local app ‘catalog-graphql-local’ (go.tf1.fr/mytf1/catalog-graphql)... ⚙ Running local app ‘catalog-api-local’ (go.tf1.fr/mytf1/catalog-api)... ... 96