DCK - Ressources

TP1. Mises à jour continues (rolling update)

Objectif : Effectuer une mise à jour progressive d’un service.

  • Déployer un service avec plusieurs replicas.
  • Modifier l’image avec une nouvelle version.
  • Déployer avec stratégie --update-parallelism et --update-delay.

TP7. Contraintes de placement et affinities

Objectif : Maîtriser la planification avancée des conteneurs.

  • Taguer les nœuds avec des labels (ex: SSD, region=france).
  • Déployer des services avec placement constraints.
  • Utiliser placement preferences pour équilibrer la charge.
Deploy services to a swarm
Deploy services to a swarm

Docker Swarm

vagrant / vagrant
root / vagrant

# Installation RHEL
sudo yum update
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum -y install vagrant
sudo yum install VirtualBox-7.0

## Créer la configuration de la première machine (dans un dossier e.g. vm1)
vagrant init debian/bookworm64
## Configure network (configurer le réseau)
nano ./Vagrantfile
# ajouter la ligne dans les configurations 
=> config.vm.network "private_network", ip: "192.168.56.10"
## Démarer la vm
vagrant up

## Créer la configuration de la deuxième machine (dans un dossier e.g. vm2)
vagrant init debian/bookworm64
## Configure network (configurer le réseau)
nano ./Vagrantfile
# ajouter la ligne dans les configurations 
=> config.vm.network "private_network", ip: "192.168.56.11"
## Démarer la vm
vagrant up

## Connection a la machine (dans le répertoire de la machine)
vagrant ssh

## Destruction d'une machine (que si vous avez fait une erreur)
vagrant destroy
## Installation Docker
sudo apt update
sudo apt install curl
curl https://get.docker.com | sudo bash -

## Master
docker swarm init
## --advertise-addr <ip>

## Worker
docker swarm join --token <token> <ip_master>

## Passer un noeud en maitre
docker node promote <noeud> 

Config Seccomp

{
  "defaultAction": "SCMP_ACT_ERRNO",
  "syscalls": [
    {
      "names": ["read", "write", "exit", "sigreturn"],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}
{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32"],
  "syscalls": [
    {
			"names": [
				"accept",
				"accept4",
				"access",
				"adjtimex",
				"alarm",
				"bind",
				"brk",
				"cachestat",
				"capget",
				"capset",
				"chdir",
				"chmod",
				"chown",
				"chown32",
				"clock_adjtime",
				"clock_adjtime64",
				"clock_getres",
				"clock_getres_time64",
				"clock_gettime",
				"clock_gettime64",
				"clock_nanosleep",
				"clock_nanosleep_time64",
				"close",
				"close_range",
				"connect",
				"copy_file_range",
				"creat",
				"dup",
				"dup2",
				"dup3",
				"epoll_create",
				"epoll_create1",
				"epoll_ctl",
				"epoll_ctl_old",
				"epoll_pwait",
				"epoll_pwait2",
				"epoll_wait",
				"epoll_wait_old",
				"eventfd",
				"eventfd2",
				"execve",
				"execveat",
				"exit",
				"exit_group",
				"faccessat",
				"faccessat2",
				"fadvise64",
				"fadvise64_64",
				"fallocate",
				"fanotify_mark",
				"fchdir",
				"fchmod",
				"fchmodat",
				"fchmodat2",
				"fchown",
				"fchown32",
				"fchownat",
				"fcntl",
				"fcntl64",
				"fdatasync",
				"fgetxattr",
				"flistxattr",
				"flock",
				"fork",
				"fremovexattr",
				"fsetxattr",
				"fstat",
				"fstat64",
				"fstatat64",
				"fstatfs",
				"fstatfs64",
				"fsync",
				"ftruncate",
				"ftruncate64",
				"futex",
				"futex_requeue",
				"futex_time64",
				"futex_wait",
				"futex_waitv",
				"futex_wake",
				"futimesat",
				"getcpu",
				"getcwd",
				"getdents",
				"getdents64",
				"getegid",
				"getegid32",
				"geteuid",
				"geteuid32",
				"getgid",
				"getgid32",
				"getgroups",
				"getgroups32",
				"getitimer",
				"getpeername",
				"getpgid",
				"getpgrp",
				"getpid",
				"getppid",
				"getpriority",
				"getrandom",
				"getresgid",
				"getresgid32",
				"getresuid",
				"getresuid32",
				"getrlimit",
				"get_robust_list",
				"getrusage",
				"getsid",
				"getsockname",
				"getsockopt",
				"get_thread_area",
				"gettid",
				"gettimeofday",
				"getuid",
				"getuid32",
				"getxattr",
				"getxattrat",
				"inotify_add_watch",
				"inotify_init",
				"inotify_init1",
				"inotify_rm_watch",
				"io_cancel",
				"ioctl",
				"io_destroy",
				"io_getevents",
				"io_pgetevents",
				"io_pgetevents_time64",
				"ioprio_get",
				"ioprio_set",
				"io_setup",
				"io_submit",
				"ipc",
				"kill",
				"landlock_add_rule",
				"landlock_create_ruleset",
				"landlock_restrict_self",
				"lchown",
				"lchown32",
				"lgetxattr",
				"link",
				"linkat",
				"listen",
				"listmount",
				"listxattr",
				"listxattrat",
				"llistxattr",
				"_llseek",
				"lremovexattr",
				"lseek",
				"lsetxattr",
				"lstat",
				"lstat64",
				"madvise",
				"map_shadow_stack",
				"membarrier",
				"memfd_create",
				"memfd_secret",
				"mincore",
				"mkdir",
				"mkdirat",
				"mknod",
				"mknodat",
				"mlock",
				"mlock2",
				"mlockall",
				"mmap",
				"mmap2",
				"mprotect",
				"mq_getsetattr",
				"mq_notify",
				"mq_open",
				"mq_timedreceive",
				"mq_timedreceive_time64",
				"mq_timedsend",
				"mq_timedsend_time64",
				"mq_unlink",
				"mremap",
				"mseal",
				"msgctl",
				"msgget",
				"msgrcv",
				"msgsnd",
				"msync",
				"munlock",
				"munlockall",
				"munmap",
				"name_to_handle_at",
				"nanosleep",
				"newfstatat",
				"_newselect",
				"open",
				"openat",
				"openat2",
				"pause",
				"pidfd_open",
				"pidfd_send_signal",
				"pipe",
				"pipe2",
				"pkey_alloc",
				"pkey_free",
				"pkey_mprotect",
				"poll",
				"ppoll",
				"ppoll_time64",
				"prctl",
				"pread64",
				"preadv",
				"preadv2",
				"prlimit64",
				"process_mrelease",
				"pselect6",
				"pselect6_time64",
				"pwrite64",
				"pwritev",
				"pwritev2",
				"read",
				"readahead",
				"readlink",
				"readlinkat",
				"readv",
				"recv",
				"recvfrom",
				"recvmmsg",
				"recvmmsg_time64",
				"recvmsg",
				"remap_file_pages",
				"removexattr",
				"removexattrat",
				"rename",
				"renameat",
				"renameat2",
				"restart_syscall",
				"riscv_hwprobe",
				"rmdir",
				"rseq",
				"rt_sigaction",
				"rt_sigpending",
				"rt_sigprocmask",
				"rt_sigqueueinfo",
				"rt_sigreturn",
				"rt_sigsuspend",
				"rt_sigtimedwait",
				"rt_sigtimedwait_time64",
				"rt_tgsigqueueinfo",
				"sched_getaffinity",
				"sched_getattr",
				"sched_getparam",
				"sched_get_priority_max",
				"sched_get_priority_min",
				"sched_getscheduler",
				"sched_rr_get_interval",
				"sched_rr_get_interval_time64",
				"sched_setaffinity",
				"sched_setattr",
				"sched_setparam",
				"sched_setscheduler",
				"sched_yield",
				"seccomp",
				"select",
				"semctl",
				"semget",
				"semop",
				"semtimedop",
				"semtimedop_time64",
				"send",
				"sendfile",
				"sendfile64",
				"sendmmsg",
				"sendmsg",
				"sendto",
				"setfsgid",
				"setfsgid32",
				"setfsuid",
				"setfsuid32",
				"setgid",
				"setgid32",
				"setgroups",
				"setgroups32",
				"setitimer",
				"setpgid",
				"setpriority",
				"setregid",
				"setregid32",
				"setresgid",
				"setresgid32",
				"setresuid",
				"setresuid32",
				"setreuid",
				"setreuid32",
				"setrlimit",
				"set_robust_list",
				"setsid",
				"setsockopt",
				"set_thread_area",
				"set_tid_address",
				"setuid",
				"setuid32",
				"setxattr",
				"setxattrat",
				"shmat",
				"shmctl",
				"shmdt",
				"shmget",
				"shutdown",
				"sigaltstack",
				"signalfd",
				"signalfd4",
				"sigprocmask",
				"sigreturn",
				"socketcall",
				"socketpair",
				"splice",
				"stat",
				"stat64",
				"statfs",
				"statfs64",
				"statmount",
				"statx",
				"symlink",
				"symlinkat",
				"sync",
				"sync_file_range",
				"syncfs",
				"sysinfo",
				"tee",
				"tgkill",
				"time",
				"timer_create",
				"timer_delete",
				"timer_getoverrun",
				"timer_gettime",
				"timer_gettime64",
				"timer_settime",
				"timer_settime64",
				"timerfd_create",
				"timerfd_gettime",
				"timerfd_gettime64",
				"timerfd_settime",
				"timerfd_settime64",
				"times",
				"tkill",
				"truncate",
				"truncate64",
				"ugetrlimit",
				"umask",
				"uname",
				"unlink",
				"unlinkat",
				"uretprobe",
				"utime",
				"utimensat",
				"utimensat_time64",
				"utimes",
				"vfork",
				"vmsplice",
				"wait4",
				"waitid",
				"waitpid",
				"write",
				"writev"
				"process_vm_readv",
				"process_vm_writev",
				"ptrace",
				"socket",
				"personality",
				"sync_file_range2",
				"swapcontext",
				"arm_fadvise64_64",
				"arm_sync_file_range",
				"sync_file_range2",
				"breakpoint",
				"cacheflush",
				"set_tls",
				"arch_prctl",
				"bpf",
				"clone",
				"clone3",
				"fanotify_init",
				"fsconfig",
				"fsmount",
				"fsopen",
				"fspick",
				"lookup_dcookie",
				"lsm_get_self_attr",
				"lsm_list_modules",
				"lsm_set_self_attr",
	"perf_event_open",
				"mount",
		"iopl",
				"ioperm",
				"mount_setattr",
				"move_mount",
				"open_tree",
				"perf_event_open",
				"quotactl",
				"quotactl_fd",
				"setdomainname",
				"sethostname",
				"setns",
				"syslog",
	"acct",
				"vhangup",
		"bpf",
 
				"umount",
		"get_mempolicy",
				"mbind",
				"set_mempolicy",
				"set_mempolicy_home_node",
				"umount2",
				"unshare",
	"syslog",
				"chroot",
				"kcmp",
				"pidfd_getfd",
				"process_madvise",
	"settimeofday",
				"stime",
				"clock_settime",
				"clock_settime64",
				"process_vm_readv",
				"process_vm_writev",
				"ptrace",
		"delete_module",
				"init_module",
				"finit_module"
			],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}

Config apparmor

#include <tunables/global>
profile docker-nginx-profile flags=(attach_disconnected,mediate_deleted) {
  capability net_bind_service,
  capability setuid,
  deny network inet,
}

Config Prometheus

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']

cAdvisor run

sudo docker run \
  --volume=/:/rootfs:ro \
  --volume=/var/run:/var/run:ro \
  --volume=/sys:/sys:ro \
  --volume=/var/lib/docker/:/var/lib/docker:ro \
  --volume=/dev/disk/:/dev/disk:ro \
  --publish=8080:8080 \
  --detach=true \
  --name=cadvisor \
  --privileged \
  --device=/dev/kmsg \
  gcr.io/cadvisor/cadvisor

Docker Daemon Config

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Logstash.conf

input {
  file {
    path => "/var/lib/docker/containers/*/*.log"
    start_position => "beginning"
    sincedb_path => "/dev/null"
  }
}

filter {
  json {
    source => "message"
  }
}

output {
  elasticsearch {
    hosts => ["http://elasticsearch:9200"]
    index => "docker-logs-%{+YYYY.MM.dd}"
  }
}

Logstash & Kibana run


docker run -d --name logstash -p 5000:5000 \
--user=root \
-v $(pwd)/logstash.conf:/usr/share/logstash/pipeline/logstash.conf \
-v /var/lib/docker/containers:/var/lib/docker/containers:ro \
logstash:7.9.3

docker run -d --name elasticsearch -p 9200:9200 -e "discovery.type=single-node" elasticsearch:7.9.3

docker run -d --name kibana \
  -p 5601:5601 \
  -e "ELASTICSEARCH_HOSTS=http://elasticsearch:9200" \
  kibana:7.9.3

docker network create logs
docker network connect logs logstash
docker network connect logs elasticsearch
docker network connect logs kibana

Dockerfile (multi-stage build)

FROM golang:1.23 AS build
WORKDIR /src
COPY <<EOF ./main.go
package main

import "fmt"

func main() {
  fmt.Println("hello, world")
}
EOF
RUN go build -o /bin/hello ./main.go

FROM scratch
COPY --from=build /bin/hello /bin/hello
CMD ["/bin/hello"]

Docker Cross platform build

Modifier / Créer le fichier /etc/docker/daemon.json

{
  "builder": {
    "gc": {
      "defaultKeepStorage": "20GB",
      "enabled": true
    }
  },
  "debug": true,
  "experimental": true
}
systemctl restart docker
docker buildx create --name mybuilder
docker buildx use mybuilder
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 .

Monitoring avec Portainer

docker volume create portainer_data
docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:2.21.5

https://localhost:9443

Docker Compose wordpress

Créer le fichier docker-compose.yaml

services:
  db:
    image: mariadb:10.6.4-focal
    command: '--default-authentication-plugin=mysql_native_password'
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=somewordpress
      - MYSQL_DATABASE=wordpress
      - MYSQL_USER=wordpress
      - MYSQL_PASSWORD=wordpress
    expose:
      - 3306
      - 33060
  wordpress:
    image: wordpress:latest
    ports:
      - 80:80
    restart: always
    environment:
      - WORDPRESS_DB_HOST=db
      - WORDPRESS_DB_USER=wordpress
      - WORDPRESS_DB_PASSWORD=wordpress
      - WORDPRESS_DB_NAME=wordpress
volumes:
  db_data:

  
    

Vlan in Docker

Example d'usage

docker network create -d ipvlan \
    --subnet=192.168.1.0/24 \
    --gateway=192.168.1.1 \
    -o ipvlan_mode=l2 \
    -o parent=eth0 db_net
docker run --net=db_net -it --rm alpine /bin/sh

Traefik

traefik.yml

entryPoints:
  web:
    address: ":80"
 
providers:
  docker:
    exposedByDefault: false
 
api:
  dashboard: true
 

Docker compose traefik

services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    restart: always
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yml:/traefik.yml:ro
    command:
      - "--configFile=/traefik.yml"
    networks:
      - traefik
 
networks:
  traefik:
    name: traefik

Wordpress 1 (compose yaml)

services:
 
  wordpress:
    image: wordpress:6.6.1-php8.2-apache
    restart: always
    environment:
      - WORDPRESS_DB_HOST=db
      - WORDPRESS_DB_USER=wordpress
      - WORDPRESS_DB_PASSWORD=wordpress
      - WORDPRESS_DB_NAME=wordpress
    volumes:
      - db-data:/var/www/html
    deploy:
      mode: replicated
      replicas: 2
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.wordpress.rule=Host(`wordpress_blog.local`)"
      - "traefik.http.routers.wordpress.entrypoints=web"
    networks:
      - traefik
  db:
    image: mariadb:10.6.4-focal
    restart: always
    environment:
      - MYSQL_DATABASE=wordpress
      - MYSQL_USER=wordpress
      - MYSQL_PASSWORD=wordpress
      - MYSQL_ROOT_PASSWORD=somewordpress
    volumes:
      - db-data:/var/lib/mysql
 
volumes:
  db-data:
 
networks:
  traefik:
    external: true

Wordpress 2 (compose yaml)

services:
 
  wordpress:
    image: wordpress:6.6.1-php8.2-apache
    restart: always
    environment:
      - WORDPRESS_DB_HOST=db
      - WORDPRESS_DB_USER=wordpress
      - WORDPRESS_DB_PASSWORD=wordpress
      - WORDPRESS_DB_NAME=wordpress
    volumes:
      - db-data:/var/www/html
    deploy:
      mode: replicated
      replicas: 2
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.wordpress2.rule=Host(`wordpress_main.local`)"
      - "traefik.http.routers.wordpress2.entrypoints=web"
    networks:
      - traefik
  db:
    image: mariadb:10.6.4-focal
    restart: always
    environment:
      - MYSQL_DATABASE=wordpress
      - MYSQL_USER=wordpress
      - MYSQL_PASSWORD=wordpress
      - MYSQL_ROOT_PASSWORD=somewordpress
    volumes:
      - db-data:/var/lib/mysql
 
volumes:
  db-data:
 
networks:
  traefik:
    external: true

Kubernetes

Créer un fichier Yaml

Le nom du fichier yaml n'est pas important

apiVersion: v1
kind: Namespace
metadata:
  name: wordpress-app

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mariadb
  namespace: wordpress-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: mariadb
  template:
    metadata:
      labels:
        app: mariadb
    spec:
      containers:
      - name: mariadb
        image: mariadb:10.6.4-focal
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "somewordpress"
        - name: MYSQL_DATABASE
          value: "wordpress"
        - name: MYSQL_USER
          value: "wordpress"
        - name: MYSQL_PASSWORD
          value: "wordpress"
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mariadb-vol
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: mariadb-vol
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "standard"
      resources:
        requests:
          storage: 1Gi

---
apiVersion: v1
kind: Service
metadata:
  name: mariadb-service
  namespace: wordpress-app
spec:
  selector:
    app: mariadb
  ports:
    - protocol: TCP
      port: 3306
      targetPort: 3306


---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  namespace: wordpress-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
      - name: wordpress
        image: wordpress:6.6.1-php8.2-apache
        env:
        - name: WORDPRESS_DB_HOST
          value: mariadb-service
        - name: WORDPRESS_DB_USER
          value: "wordpress"
        - name: WORDPRESS_DB_PASSWORD
          value: "wordpress"
        - name: WORDPRESS_DB_NAME
          value: "wordpress"
        ports:
        - containerPort: 80
          name: http
---
apiVersion: v1
kind: Service
metadata:
  name: wordpress-service
  namespace: wordpress-app
spec:
  type: ClusterIP
  selector:
    app: wordpress
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

Mise en place de l'environement

kubectl apply -f ./nom_de_votre_fichier.yaml

Port-forward (obligatoire sur les environement de dev local NE PAS EXPLOITER EN PROD)

kubectl port-forward service/nom_du_service port_local:port_cible

## Wordpress
kubectl port-forward -n wordpress-app service/wordpress-service 8080:80

minikube addons enable metrics-server

Multi instance

minikube addons enable ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: wordpress-ingress
  namespace: wordpress-app
spec:
  ingressClassName: nginx
  rules:
    - host: wordpress1.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: wordpress-service
                port:
                  number: 80
minikube ip

# Editer le fichier /etc/hosts sur la machine hôte
# Rajouter la ligne
votre_ip wordpress1.local

http://wordpress1.local:80

HPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: wordpress-hpa
  namespace: wordpress-app
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: wordpress
  minReplicas: 1
  maxReplicas: 5
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50