Docker dhe gjithçka, gjithçka, gjithçka

TL;DR: Një udhëzues përmbledhës për krahasimin e kornizave për ekzekutimin e aplikacioneve në kontejnerë. Do të merren parasysh aftësitë e Docker dhe sistemeve të tjera të ngjashme.

Docker dhe gjithçka, gjithçka, gjithçka

Një histori e vogël se nga erdhi e gjitha

Histori

Metoda e parë e njohur për izolimin e një aplikacioni është chroot. Thirrja e sistemit me të njëjtin emër siguron ndryshimin e direktoriumit rrënjë - duke siguruar kështu që programi që e ka thirrur të ketë akses vetëm në skedarët brenda atij drejtorie. Por nëse një programi i jepen privilegje rrënjësore nga brenda, ai potencialisht mund të "shpëtuar" nga chroot dhe të fitojë akses në sistemin kryesor operativ. Gjithashtu, përveç ndryshimit të direktoriumit rrënjë, burimet e tjera (RAM, procesori), si dhe aksesi në rrjet nuk janë të kufizuara.

Metoda tjetër është lëshimi i një sistemi operativ të plotë brenda një kontejneri, duke përdorur mekanizmat e kernelit të sistemit operativ. Kjo metodë quhet ndryshe në sisteme të ndryshme operative, por thelbi është i njëjtë - lëshimi i disa sistemeve operative të pavarura, secila prej të cilave ekzekuton të njëjtin kernel në të cilin funksionon sistemi kryesor operativ. Këto përfshijnë FreeBSD Jails, Solaris Zones, OpenVZ dhe LXC për Linux. Izolimi sigurohet jo vetëm nga hapësira në disk, por edhe nga burime të tjera; në veçanti, çdo kontejner mund të ketë kufizime në kohën e procesorit, RAM-in dhe gjerësinë e brezit të rrjetit. Krahasuar me chroot, largimi nga kontejneri është më i vështirë, pasi superpërdoruesi në kontejner ka qasje vetëm në përmbajtjen e kontejnerit, megjithatë, për shkak të nevojës për të mbajtur të përditësuar sistemin operativ brenda kontejnerit dhe përdorimin e versioneve më të vjetra. e kerneleve (relevante për Linux-in, në një masë më të vogël FreeBSD), ekziston një gjasë jo zero për të "depërtuar" sistemin e izolimit të kernelit dhe për të fituar akses në sistemin kryesor operativ.

Në vend që të nisni një sistem operativ të plotë në një enë (me një sistem inicializimi, menaxher paketash, etj.), Ju mund të nisni aplikacionet menjëherë, gjëja kryesore është t'u jepni aplikacioneve një mundësi të tillë (prania e bibliotekave të nevojshme dhe skedarë të tjerë). Kjo ide shërbeu si bazë për virtualizimin e aplikacioneve të kontejnerizuar, përfaqësuesi më i shquar dhe më i njohur i të cilit është Docker. Krahasuar me sistemet e mëparshme, mekanizmat më fleksibël të izolimit, së bashku me mbështetjen e integruar për rrjetet virtuale midis kontejnerëve dhe gjurmimin e gjendjes së aplikacionit brenda kontejnerit, rezultuan në aftësinë për të ndërtuar një mjedis të vetëm koherent nga një numër i madh serverësh fizikë për funksionimin e kontejnerëve - pa pasur nevojë për menaxhim manual të burimeve.

prerës

Docker është programi më i famshëm i kontejnerizimit të aplikacioneve. I shkruar në gjuhën Go, ai përdor veçoritë standarde të kernelit Linux - cgroups, hapësirat e emrave, aftësitë, etj., si dhe sistemet e skedarëve Aufs dhe të tjera të ngjashme për të kursyer hapësirën në disk.

Docker dhe gjithçka, gjithçka, gjithçka
Burimi: wikimedia

Arkitekturë

Përpara versionit 1.11, Docker punonte si një shërbim i vetëm që kryente të gjitha operacionet me kontejnerë: shkarkimin e imazheve për kontejnerë, lëshimin e kontejnerëve, përpunimin e kërkesave API. Duke filluar me versionin 1.11, Docker u nda në disa pjesë që ndërveprojnë me njëra-tjetrën: kontejneri, për përpunimin e të gjithë ciklit jetësor të kontejnerëve (ndarja e hapësirës në disk, shkarkimi i imazheve, puna me rrjetin, nisja, instalimi dhe monitorimi i gjendjes së kontejnerëve) dhe runC, mjedisi i ekzekutimit të kontejnerit, bazuar në përdorimin e cgroups dhe veçorive të tjera të kernelit Linux. Vetë shërbimi docker mbetet, por tani shërben vetëm për të përpunuar kërkesat API të përkthyera në kontejner.

Docker dhe gjithçka, gjithçka, gjithçka

Instalimi dhe konfigurimi

Mënyra ime e preferuar për të instaluar docker është docker-machine, e cila, përveç instalimit dhe konfigurimit të drejtpërdrejtë të docker-it në serverët në distancë (duke përfshirë retë e ndryshme), bën të mundur punën me sistemet e skedarëve të serverëve në distancë dhe gjithashtu mund të ekzekutojë komanda të ndryshme.

Megjithatë, që nga viti 2018, projekti pothuajse nuk është zhvilluar, kështu që ne do ta instalojmë atë në mënyrën standarde për shumicën e shpërndarjeve Linux - duke shtuar një depo dhe duke instaluar paketat e nevojshme.

Kjo metodë përdoret gjithashtu për instalim të automatizuar, për shembull duke përdorur Ansible ose sisteme të tjera të ngjashme, por unë nuk do ta konsideroj atë në këtë artikull.

Instalimi do të kryhet në Centos 7, unë do të përdor një makinë virtuale si server, për të instaluar thjesht ekzekutoni komandat e mëposhtme:

# yum install -y yum-utils
# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# yum install docker-ce docker-ce-cli containerd.io

Pas instalimit, duhet të filloni shërbimin dhe ta vendosni atë në fillim:

# systemctl enable docker
# systemctl start docker
# firewall-cmd --zone=public --add-port=2377/tcp --permanent

Për më tepër, ju mund të krijoni një grup docker, përdoruesit e të cilit do të jenë në gjendje të punojnë me docker pa sudo, të konfigurojnë regjistrimin, të mundësojnë hyrjen në API nga jashtë dhe mos harroni të konfiguroni murin e zjarrit më saktë (gjithçka që nuk lejohet është e ndaluar në shembujt e mësipërm dhe më poshtë - e kam lënë këtë për thjeshtësi dhe qartësi), por nuk do të hyj në më shumë detaje këtu.

Karakteristika te tjera

Përveç makinës docker të lartpërmendur, ekziston edhe regjistri docker, një mjet për ruajtjen e imazheve për kontejnerë, si dhe docker compose, një mjet për automatizimin e vendosjes së aplikacioneve në kontejnerë, skedarët YAML përdoren për të ndërtuar dhe konfiguruar kontejnerë dhe gjëra të tjera të lidhura (për shembull, rrjetet, sistemet e skedarëve të vazhdueshëm për ruajtjen e të dhënave).

Mund të përdoret gjithashtu për të organizuar transportues për CICD. Një veçori tjetër interesante është puna në modalitetin e grupit, i ashtuquajturi modaliteti swarm (përpara versionit 1.12 njihej si tufa docker), i cili ju lejon të grumbulloni një infrastrukturë të vetme nga disa serverë për drejtimin e kontejnerëve. Ekziston mbështetje për një rrjet virtual në krye të të gjithë serverëve, ka një balancues të integruar të ngarkesës, si dhe mbështetje për sekretet për kontejnerët.

Skedarët YAML nga docker compose, me modifikime të vogla, mund të përdoren për grupime të tilla, duke automatizuar plotësisht mirëmbajtjen e grupimeve të vogla dhe të mesme për qëllime të ndryshme. Për grupime të mëdha, Kubernetes është i preferueshëm, sepse kostot e mirëmbajtjes së modalitetit swarm mund të tejkalojnë ato të Kubernetes. Përveç runC, mund të instaloni, për shembull, si mjedisin e ekzekutimit të kontejnerit Kontejnerët Kata

Duke punuar me Docker

Pas instalimit dhe konfigurimit, ne do të përpiqemi të mbledhim një grup në të cilin do të vendosim GitLab dhe Docker Registry për ekipin e zhvillimit. Unë do të përdor tre makina virtuale si serverë, në të cilët do të vendos gjithashtu FS GlusterFS-in e shpërndarë; do ta përdor atë si një ruajtje të vëllimeve docker, për shembull, për të ekzekutuar një version tolerant ndaj gabimeve të regjistrit të dokerit. Komponentët kryesorë për të ekzekutuar: Docker Registry, Postgresql, Redis, GitLab me mbështetje për GitLab Runner në krye të Swarm. Ne do të lançojmë Postgresql me grupim Stolon, kështu që nuk keni nevojë të përdorni GlusterFS për të ruajtur të dhënat Postgresql. Të dhënat e mbetura kritike do të ruhen në GlusterFS.

Për të vendosur GlusterFS në të gjithë serverët (ata quhen node1, node2, node3), duhet të instaloni paketa, të aktivizoni murin e zjarrit dhe të krijoni drejtoritë e nevojshme:

# yum -y install centos-release-gluster7
# yum -y install glusterfs-server
# systemctl enable glusterd
# systemctl start glusterd
# firewall-cmd --add-service=glusterfs --permanent
# firewall-cmd --reload
# mkdir -p /srv/gluster
# mkdir -p /srv/docker
# echo "$(hostname):/docker /srv/docker glusterfs defaults,_netdev 0 0" >> /etc/fstab

Pas instalimit, puna për konfigurimin e GlusterFS duhet të vazhdojë nga një nyje, për shembull nyja 1:

# gluster peer probe node2
# gluster peer probe node3
# gluster volume create docker replica 3 node1:/srv/gluster node2:/srv/gluster node3:/srv/gluster force
# gluster volume start docker

Pastaj ju duhet të montoni vëllimin që rezulton (komandimi duhet të ekzekutohet në të gjithë serverët):

# mount /srv/docker

Modaliteti swarm është konfiguruar në një nga serverët, i cili do të jetë Leader, pjesa tjetër do të duhet të bashkohet me grupin, kështu që rezultati i ekzekutimit të komandës në serverin e parë do të duhet të kopjohet dhe ekzekutohet në të tjerët.

Konfigurimi fillestar i grupit, unë ekzekutoj komandën në node1:

# docker swarm init
Swarm initialized: current node (a5jpfrh5uvo7svzz1ajduokyq) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-0c5mf7mvzc7o7vjk0wngno2dy70xs95tovfxbv4tqt9280toku-863hyosdlzvd76trfptd4xnzd xx.xx.xx.xx:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
# docker swarm join-token manager

Ne kopjojmë rezultatin e komandës së dytë dhe e ekzekutojmë atë në node2 dhe node3:

# docker swarm join --token SWMTKN-x-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxx xx.xx.xx.xx:2377
This node joined a swarm as a manager.

Në këtë pikë, konfigurimi paraprak i serverëve ka përfunduar, le të vazhdojmë me konfigurimin e shërbimeve; komandat që do të ekzekutohen do të nisen nga nyja 1, përveç nëse specifikohet ndryshe.

Para së gjithash, le të krijojmë rrjete për kontejnerë:

# docker network create --driver=overlay etcd
# docker network create --driver=overlay pgsql
# docker network create --driver=overlay redis
# docker network create --driver=overlay traefik
# docker network create --driver=overlay gitlab

Pastaj ne shënojmë serverët, kjo është e nevojshme për të lidhur disa shërbime me serverët:

# docker node update --label-add nodename=node1 node1
# docker node update --label-add nodename=node2 node2
# docker node update --label-add nodename=node3 node3

Më pas, ne krijojmë direktori për ruajtjen e të dhënave etcd, ruajtjen e KV, e cila është e nevojshme për Traefik dhe Stolon. Ngjashëm me Postgresql, këto do të jenë kontejnerë të lidhur me serverët, kështu që ne ekzekutojmë këtë komandë në të gjithë serverët:

# mkdir -p /srv/etcd

Më pas, krijoni një skedar për të konfiguruar etcd dhe përdorni atë:

00etcd.yml

version: '3.7'

services:
  etcd1:
    image: quay.io/coreos/etcd:latest
    hostname: etcd1
    command:
      - etcd
      - --name=etcd1
      - --data-dir=/data.etcd
      - --advertise-client-urls=http://etcd1:2379
      - --listen-client-urls=http://0.0.0.0:2379
      - --initial-advertise-peer-urls=http://etcd1:2380
      - --listen-peer-urls=http://0.0.0.0:2380
      - --initial-cluster=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
      - --initial-cluster-state=new
      - --initial-cluster-token=etcd-cluster
    networks:
      - etcd
    volumes:
      - etcd1vol:/data.etcd
    deploy:
      replicas: 1
      placement:
        constraints: [node.labels.nodename == node1]
  etcd2:
    image: quay.io/coreos/etcd:latest
    hostname: etcd2
    command:
      - etcd
      - --name=etcd2
      - --data-dir=/data.etcd
      - --advertise-client-urls=http://etcd2:2379
      - --listen-client-urls=http://0.0.0.0:2379
      - --initial-advertise-peer-urls=http://etcd2:2380
      - --listen-peer-urls=http://0.0.0.0:2380
      - --initial-cluster=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
      - --initial-cluster-state=new
      - --initial-cluster-token=etcd-cluster
    networks:
      - etcd
    volumes:
      - etcd2vol:/data.etcd
    deploy:
      replicas: 1
      placement:
        constraints: [node.labels.nodename == node2]
  etcd3:
    image: quay.io/coreos/etcd:latest
    hostname: etcd3
    command:
      - etcd
      - --name=etcd3
      - --data-dir=/data.etcd
      - --advertise-client-urls=http://etcd3:2379
      - --listen-client-urls=http://0.0.0.0:2379
      - --initial-advertise-peer-urls=http://etcd3:2380
      - --listen-peer-urls=http://0.0.0.0:2380
      - --initial-cluster=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
      - --initial-cluster-state=new
      - --initial-cluster-token=etcd-cluster
    networks:
      - etcd
    volumes:
      - etcd3vol:/data.etcd
    deploy:
      replicas: 1
      placement:
        constraints: [node.labels.nodename == node3]

volumes:
  etcd1vol:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/etcd"
  etcd2vol:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/etcd"
  etcd3vol:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/etcd"

networks:
  etcd:
    external: true

# docker stack deploy --compose-file 00etcd.yml etcd

Pas ca kohësh, kontrollojmë që grupi etcd është lart:

# docker exec $(docker ps | awk '/etcd/ {print $1}')  etcdctl member list
ade526d28b1f92f7: name=etcd1 peerURLs=http://etcd1:2380 clientURLs=http://etcd1:2379 isLeader=false
bd388e7810915853: name=etcd3 peerURLs=http://etcd3:2380 clientURLs=http://etcd3:2379 isLeader=false
d282ac2ce600c1ce: name=etcd2 peerURLs=http://etcd2:2380 clientURLs=http://etcd2:2379 isLeader=true
# docker exec $(docker ps | awk '/etcd/ {print $1}')  etcdctl cluster-health
member ade526d28b1f92f7 is healthy: got healthy result from http://etcd1:2379
member bd388e7810915853 is healthy: got healthy result from http://etcd3:2379
member d282ac2ce600c1ce is healthy: got healthy result from http://etcd2:2379
cluster is healthy

Ne krijojmë drejtori për Postgresql, ekzekutojmë komandën në të gjithë serverët:

# mkdir -p /srv/pgsql

Tjetra, krijoni një skedar për të konfiguruar Postgresql:

01pgsql.yml

version: '3.7'

services:
  pgsentinel:
    image: sorintlab/stolon:master-pg10
    command:
      - gosu
      - stolon
      - stolon-sentinel
      - --cluster-name=stolon-cluster
      - --store-backend=etcdv3
      - --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
      - --log-level=debug
    networks:
      - etcd
      - pgsql
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 30s
        order: stop-first
        failure_action: pause
  pgkeeper1:
    image: sorintlab/stolon:master-pg10
    hostname: pgkeeper1
    command:
      - gosu
      - stolon
      - stolon-keeper
      - --pg-listen-address=pgkeeper1
      - --pg-repl-username=replica
      - --uid=pgkeeper1
      - --pg-su-username=postgres
      - --pg-su-passwordfile=/run/secrets/pgsql
      - --pg-repl-passwordfile=/run/secrets/pgsql_repl
      - --data-dir=/var/lib/postgresql/data
      - --cluster-name=stolon-cluster
      - --store-backend=etcdv3
      - --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
    networks:
      - etcd
      - pgsql
    environment:
      - PGDATA=/var/lib/postgresql/data
    volumes:
      - pgkeeper1:/var/lib/postgresql/data
    secrets:
      - pgsql
      - pgsql_repl
    deploy:
      replicas: 1
      placement:
        constraints: [node.labels.nodename == node1]
  pgkeeper2:
    image: sorintlab/stolon:master-pg10
    hostname: pgkeeper2
    command:
      - gosu
      - stolon 
      - stolon-keeper
      - --pg-listen-address=pgkeeper2
      - --pg-repl-username=replica
      - --uid=pgkeeper2
      - --pg-su-username=postgres
      - --pg-su-passwordfile=/run/secrets/pgsql
      - --pg-repl-passwordfile=/run/secrets/pgsql_repl
      - --data-dir=/var/lib/postgresql/data
      - --cluster-name=stolon-cluster
      - --store-backend=etcdv3
      - --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
    networks:
      - etcd
      - pgsql
    environment:
      - PGDATA=/var/lib/postgresql/data
    volumes:
      - pgkeeper2:/var/lib/postgresql/data
    secrets:
      - pgsql
      - pgsql_repl
    deploy:
      replicas: 1
      placement:
        constraints: [node.labels.nodename == node2]
  pgkeeper3:
    image: sorintlab/stolon:master-pg10
    hostname: pgkeeper3
    command:
      - gosu
      - stolon 
      - stolon-keeper
      - --pg-listen-address=pgkeeper3
      - --pg-repl-username=replica
      - --uid=pgkeeper3
      - --pg-su-username=postgres
      - --pg-su-passwordfile=/run/secrets/pgsql
      - --pg-repl-passwordfile=/run/secrets/pgsql_repl
      - --data-dir=/var/lib/postgresql/data
      - --cluster-name=stolon-cluster
      - --store-backend=etcdv3
      - --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
    networks:
      - etcd
      - pgsql
    environment:
      - PGDATA=/var/lib/postgresql/data
    volumes:
      - pgkeeper3:/var/lib/postgresql/data
    secrets:
      - pgsql
      - pgsql_repl
    deploy:
      replicas: 1
      placement:
        constraints: [node.labels.nodename == node3]
  postgresql:
    image: sorintlab/stolon:master-pg10
    command: gosu stolon stolon-proxy --listen-address 0.0.0.0 --cluster-name stolon-cluster --store-backend=etcdv3 --store-endpoints http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
    networks:
      - etcd
      - pgsql
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 30s
        order: stop-first
        failure_action: rollback

volumes:
  pgkeeper1:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/pgsql"
  pgkeeper2:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/pgsql"
  pgkeeper3:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/pgsql"

secrets:
  pgsql:
    file: "/srv/docker/postgres"
  pgsql_repl:
    file: "/srv/docker/replica"

networks:
  etcd:
    external: true
  pgsql:
    external: true

Ne gjenerojmë sekrete dhe përdorim skedarin:

# </dev/urandom tr -dc 234567890qwertyuopasdfghjkzxcvbnmQWERTYUPASDFGHKLZXCVBNM | head -c $(((RANDOM%3)+15)) > /srv/docker/replica
# </dev/urandom tr -dc 234567890qwertyuopasdfghjkzxcvbnmQWERTYUPASDFGHKLZXCVBNM | head -c $(((RANDOM%3)+15)) > /srv/docker/postgres
# docker stack deploy --compose-file 01pgsql.yml pgsql

Pas ca kohësh (shiko daljen e komandës shërbimi docker lsqë të gjitha shërbimet janë në funksion) ne inicializojmë grupin Postgresql:

# docker exec $(docker ps | awk '/pgkeeper/ {print $1}') stolonctl --cluster-name=stolon-cluster --store-backend=etcdv3 --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379 init

Kontrollimi i gatishmërisë së grupit Postgresql:

# docker exec $(docker ps | awk '/pgkeeper/ {print $1}') stolonctl --cluster-name=stolon-cluster --store-backend=etcdv3 --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379 status
=== Active sentinels ===

ID      LEADER
26baa11d    false
74e98768    false
a8cb002b    true

=== Active proxies ===

ID
4d233826
9f562f3b
b0c79ff1

=== Keepers ===

UID     HEALTHY PG LISTENADDRESS    PG HEALTHY  PG WANTEDGENERATION PG CURRENTGENERATION
pgkeeper1   true    pgkeeper1:5432         true     2           2
pgkeeper2   true    pgkeeper2:5432          true            2                   2
pgkeeper3   true    pgkeeper3:5432          true            3                   3

=== Cluster Info ===

Master Keeper: pgkeeper3

===== Keepers/DB tree =====

pgkeeper3 (master)
├─pgkeeper2
└─pgkeeper1

Ne konfigurojmë traefik për të hapur hyrjen në kontejnerë nga jashtë:

03traefik.yml

version: '3.7'

services:
  traefik:
    image: traefik:latest
    command: >
      --log.level=INFO
      --providers.docker=true
      --entryPoints.web.address=:80
      --providers.providersThrottleDuration=2
      --providers.docker.watch=true
      --providers.docker.swarmMode=true
      --providers.docker.swarmModeRefreshSeconds=15s
      --providers.docker.exposedbydefault=false
      --accessLog.bufferingSize=0
      --api=true
      --api.dashboard=true
      --api.insecure=true
    networks:
      - traefik
    ports:
      - 80:80
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    deploy:
      replicas: 3
      placement:
        constraints:
          - node.role == manager
        preferences:
          - spread: node.id
      labels:
        - traefik.enable=true
        - traefik.http.routers.traefik.rule=Host(`traefik.example.com`)
        - traefik.http.services.traefik.loadbalancer.server.port=8080
        - traefik.docker.network=traefik

networks:
  traefik:
    external: true

# docker stack deploy --compose-file 03traefik.yml traefik

Ne nisim Redis Cluster, për ta bërë këtë ne krijojmë një drejtori ruajtjeje në të gjitha nyjet:

# mkdir -p /srv/redis

05redis.yml

version: '3.7'

services:
  redis-master:
    image: 'bitnami/redis:latest'
    networks:
      - redis
    ports:
      - '6379:6379'
    environment:
      - REDIS_REPLICATION_MODE=master
      - REDIS_PASSWORD=xxxxxxxxxxx
    deploy:
      mode: global
      restart_policy:
        condition: any
    volumes:
      - 'redis:/opt/bitnami/redis/etc/'

  redis-replica:
    image: 'bitnami/redis:latest'
    networks:
      - redis
    ports:
      - '6379'
    depends_on:
      - redis-master
    environment:
      - REDIS_REPLICATION_MODE=slave
      - REDIS_MASTER_HOST=redis-master
      - REDIS_MASTER_PORT_NUMBER=6379
      - REDIS_MASTER_PASSWORD=xxxxxxxxxxx
      - REDIS_PASSWORD=xxxxxxxxxxx
    deploy:
      mode: replicated
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: any

  redis-sentinel:
    image: 'bitnami/redis:latest'
    networks:
      - redis
    ports:
      - '16379'
    depends_on:
      - redis-master
      - redis-replica
    entrypoint: |
      bash -c 'bash -s <<EOF
      "/bin/bash" -c "cat <<EOF > /opt/bitnami/redis/etc/sentinel.conf
      port 16379
      dir /tmp
      sentinel monitor master-node redis-master 6379 2
      sentinel down-after-milliseconds master-node 5000
      sentinel parallel-syncs master-node 1
      sentinel failover-timeout master-node 5000
      sentinel auth-pass master-node xxxxxxxxxxx
      sentinel announce-ip redis-sentinel
      sentinel announce-port 16379
      EOF"
      "/bin/bash" -c "redis-sentinel /opt/bitnami/redis/etc/sentinel.conf"
      EOF'
    deploy:
      mode: global
      restart_policy:
        condition: any

volumes:
  redis:
    driver: local
    driver_opts:
      type: 'none'
      o: 'bind'
      device: "/srv/redis"

networks:
  redis:
    external: true

# docker stack deploy --compose-file 05redis.yml redis

Shto Regjistrin Docker:

06registry.yml

version: '3.7'

services:
  registry:
    image: registry:2.6
    networks:
      - traefik
    volumes:
      - registry_data:/var/lib/registry
    deploy:
      replicas: 1
      placement:
        constraints: [node.role == manager]
      restart_policy:
        condition: on-failure
      labels:
        - traefik.enable=true
        - traefik.http.routers.registry.rule=Host(`registry.example.com`)
        - traefik.http.services.registry.loadbalancer.server.port=5000
        - traefik.docker.network=traefik

volumes:
  registry_data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/docker/registry"

networks:
  traefik:
    external: true

# mkdir /srv/docker/registry
# docker stack deploy --compose-file 06registry.yml registry

Dhe së fundi - GitLab:

08gitlab-runner.yml

version: '3.7'

services:
  gitlab:
    image: gitlab/gitlab-ce:latest
    networks:
      - pgsql
      - redis
      - traefik
      - gitlab
    ports:
      - 22222:22
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        postgresql['enable'] = false
        redis['enable'] = false
        gitlab_rails['registry_enabled'] = false
        gitlab_rails['db_username'] = "gitlab"
        gitlab_rails['db_password'] = "XXXXXXXXXXX"
        gitlab_rails['db_host'] = "postgresql"
        gitlab_rails['db_port'] = "5432"
        gitlab_rails['db_database'] = "gitlab"
        gitlab_rails['db_adapter'] = 'postgresql'
        gitlab_rails['db_encoding'] = 'utf8'
        gitlab_rails['redis_host'] = 'redis-master'
        gitlab_rails['redis_port'] = '6379'
        gitlab_rails['redis_password'] = 'xxxxxxxxxxx'
        gitlab_rails['smtp_enable'] = true
        gitlab_rails['smtp_address'] = "smtp.yandex.ru"
        gitlab_rails['smtp_port'] = 465
        gitlab_rails['smtp_user_name'] = "[email protected]"
        gitlab_rails['smtp_password'] = "xxxxxxxxx"
        gitlab_rails['smtp_domain'] = "example.com"
        gitlab_rails['gitlab_email_from'] = '[email protected]'
        gitlab_rails['smtp_authentication'] = "login"
        gitlab_rails['smtp_tls'] = true
        gitlab_rails['smtp_enable_starttls_auto'] = true
        gitlab_rails['smtp_openssl_verify_mode'] = 'peer'
        external_url 'http://gitlab.example.com/'
        gitlab_rails['gitlab_shell_ssh_port'] = 22222
    volumes:
      - gitlab_conf:/etc/gitlab
      - gitlab_logs:/var/log/gitlab
      - gitlab_data:/var/opt/gitlab
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints:
        - node.role == manager
      labels:
        - traefik.enable=true
        - traefik.http.routers.gitlab.rule=Host(`gitlab.example.com`)
        - traefik.http.services.gitlab.loadbalancer.server.port=80
        - traefik.docker.network=traefik
  gitlab-runner:
    image: gitlab/gitlab-runner:latest
    networks:
      - gitlab
    volumes:
      - gitlab_runner_conf:/etc/gitlab
      - /var/run/docker.sock:/var/run/docker.sock
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints:
        - node.role == manager

volumes:
  gitlab_conf:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/docker/gitlab/conf"
  gitlab_logs:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/docker/gitlab/logs"
  gitlab_data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/docker/gitlab/data"
  gitlab_runner_conf:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/docker/gitlab/runner"

networks:
  pgsql:
    external: true
  redis:
    external: true
  traefik:
    external: true
  gitlab:
    external: true

# mkdir -p /srv/docker/gitlab/conf
# mkdir -p /srv/docker/gitlab/logs
# mkdir -p /srv/docker/gitlab/data
# mkdir -p /srv/docker/gitlab/runner
# docker stack deploy --compose-file 08gitlab-runner.yml gitlab

Gjendja përfundimtare e grupit dhe shërbimeve:

# docker service ls
ID                  NAME                   MODE                REPLICAS            IMAGE                          PORTS
lef9n3m92buq        etcd_etcd1             replicated          1/1                 quay.io/coreos/etcd:latest
ij6uyyo792x5        etcd_etcd2             replicated          1/1                 quay.io/coreos/etcd:latest
fqttqpjgp6pp        etcd_etcd3             replicated          1/1                 quay.io/coreos/etcd:latest
hq5iyga28w33        gitlab_gitlab          replicated          1/1                 gitlab/gitlab-ce:latest        *:22222->22/tcp
dt7s6vs0q4qc        gitlab_gitlab-runner   replicated          1/1                 gitlab/gitlab-runner:latest
k7uoezno0h9n        pgsql_pgkeeper1        replicated          1/1                 sorintlab/stolon:master-pg10
cnrwul4r4nse        pgsql_pgkeeper2        replicated          1/1                 sorintlab/stolon:master-pg10
frflfnpty7tr        pgsql_pgkeeper3        replicated          1/1                 sorintlab/stolon:master-pg10
x7pqqchi52kq        pgsql_pgsentinel       replicated          3/3                 sorintlab/stolon:master-pg10
mwu2wl8fti4r        pgsql_postgresql       replicated          3/3                 sorintlab/stolon:master-pg10
9hkbe2vksbzb        redis_redis-master     global              3/3                 bitnami/redis:latest           *:6379->6379/tcp
l88zn8cla7dc        redis_redis-replica    replicated          3/3                 bitnami/redis:latest           *:30003->6379/tcp
1utp309xfmsy        redis_redis-sentinel   global              3/3                 bitnami/redis:latest           *:30002->16379/tcp
oteb824ylhyp        registry_registry      replicated          1/1                 registry:2.6
qovrah8nzzu8        traefik_traefik        replicated          3/3                 traefik:latest                 *:80->80/tcp, *:443->443/tcp

Çfarë tjetër mund të përmirësohet? Sigurohuni që të konfiguroni Traefik për të drejtuar kontejnerët mbi https, shtoni enkriptim tls për Postgresql dhe Redis. Por në përgjithësi, ajo tashmë mund t'u jepet zhvilluesve si një PoC. Le të shohim tani alternativat për Docker.

podman

Një tjetër motor mjaft i njohur për drejtimin e kontejnerëve të grupuar sipas pods (pods, grupe kontejnerësh të vendosur së bashku). Ndryshe nga Docker, nuk kërkon ndonjë shërbim për të drejtuar kontejnerët; e gjithë puna kryhet përmes bibliotekës libpod. E shkruar gjithashtu në Go, kërkon një kohë ekzekutimi të pajtueshme me OCI për të ekzekutuar kontejnerët, të tillë si runC.

Docker dhe gjithçka, gjithçka, gjithçka

Puna me Podman në përgjithësi të kujton atë për Docker, deri në pikën që mund ta bësh kështu (siç thuhet nga shumë që e kanë provuar, përfshirë autorin e këtij artikulli):

$ alias docker=podman

dhe mund të vazhdoni të punoni. Në përgjithësi, situata me Podman është shumë interesante, sepse nëse versionet e hershme të Kubernetes funksionuan me Docker, atëherë rreth vitit 2015, pas standardizimit të botës së kontejnerëve (OCI - Open Container Initiative) dhe ndarjes së Docker në kontejner dhe runC, një alternativë ndaj Docker për drejtimin në Kubernetes është zhvilluar: CRI-O. Podman në këtë drejtim është një alternativë ndaj Docker, e ndërtuar mbi parimet e Kubernetes, duke përfshirë grupimin e kontejnerëve, por qëllimi kryesor i projektit është të lëshojë kontejnerë të stilit Docker pa shërbime shtesë. Për arsye të dukshme, nuk ka modalitet tufë, pasi zhvilluesit thonë qartë se nëse keni nevojë për një grup, merrni Kubernetes.

Instalim

Për të instaluar në Centos 7, thjesht aktivizoni depon e Extras dhe më pas instaloni gjithçka me komandën:

# yum -y install podman

Karakteristika te tjera

Podman mund të gjenerojë njësi për systemd, duke zgjidhur kështu problemin e nisjes së kontejnerëve pas një rindezjeje të serverit. Për më tepër, systemd deklarohet se funksionon si duhet si pid 1 në kontejner. Ekziston një mjet i veçantë buildah për ndërtimin e kontejnerëve, ka edhe mjete të palëve të treta - analoge të docker-compose, të cilat gjithashtu gjenerojnë skedarë konfigurimi të pajtueshëm me Kubernetes, kështu që kalimi nga Podman në Kubernetes thjeshtohet sa më shumë që të jetë e mundur.

Duke punuar me Podman

Meqenëse nuk ka modalitet swarm (supozohet të kalojmë në Kubernetes nëse nevojitet një grup), ne do ta mbledhim atë në kontejnerë të veçantë.

Instaloni podman-compose:

# yum -y install python3-pip
# pip3 install podman-compose

Skedari i konfigurimit që rezulton për podman është paksa i ndryshëm, kështu që për shembull na u desh të zhvendosnim një seksion të veçantë vëllimesh direkt në seksionin me shërbime.

gitlab-podman.yml

version: '3.7'

services:
  gitlab:
    image: gitlab/gitlab-ce:latest
    hostname: gitlab.example.com
    restart: unless-stopped
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        gitlab_rails['gitlab_shell_ssh_port'] = 22222
    ports:
      - "80:80"
      - "22222:22"
    volumes:
      - /srv/podman/gitlab/conf:/etc/gitlab
      - /srv/podman/gitlab/data:/var/opt/gitlab
      - /srv/podman/gitlab/logs:/var/log/gitlab
    networks:
      - gitlab

  gitlab-runner:
    image: gitlab/gitlab-runner:alpine
    restart: unless-stopped
    depends_on:
      - gitlab
    volumes:
      - /srv/podman/gitlab/runner:/etc/gitlab-runner
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - gitlab

networks:
  gitlab:

# podman-compose -f gitlab-runner.yml -d up

Rezultati:

# podman ps
CONTAINER ID  IMAGE                                  COMMAND               CREATED             STATUS                 PORTS                                      NAMES
da53da946c01  docker.io/gitlab/gitlab-runner:alpine  run --user=gitlab...  About a minute ago  Up About a minute ago  0.0.0.0:22222->22/tcp, 0.0.0.0:80->80/tcp  root_gitlab-runner_1
781c0103c94a  docker.io/gitlab/gitlab-ce:latest      /assets/wrapper       About a minute ago  Up About a minute ago  0.0.0.0:22222->22/tcp, 0.0.0.0:80->80/tcp  root_gitlab_1

Le të shohim se çfarë gjeneron për systemd dhe kubernetes, për këtë duhet të zbulojmë emrin ose ID-në e pod:

# podman pod ls
POD ID         NAME   STATUS    CREATED          # OF CONTAINERS   INFRA ID
71fc2b2a5c63   root   Running   11 minutes ago   3                 db40ab8bf84b

Kubernetes:

# podman generate kube 71fc2b2a5c63
# Generation of Kubernetes YAML is still under development!
#
# Save the output of this file and use kubectl create -f to import
# it into Kubernetes.
#
# Created with podman-1.6.4
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2020-07-29T19:22:40Z"
  labels:
    app: root
  name: root
spec:
  containers:
  - command:
    - /assets/wrapper
    env:
    - name: PATH
      value: /opt/gitlab/embedded/bin:/opt/gitlab/bin:/assets:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    - name: TERM
      value: xterm
    - name: HOSTNAME
      value: gitlab.example.com
    - name: container
      value: podman
    - name: GITLAB_OMNIBUS_CONFIG
      value: |
        gitlab_rails['gitlab_shell_ssh_port'] = 22222
    - name: LANG
      value: C.UTF-8
    image: docker.io/gitlab/gitlab-ce:latest
    name: rootgitlab1
    ports:
    - containerPort: 22
      hostPort: 22222
      protocol: TCP
    - containerPort: 80
      hostPort: 80
      protocol: TCP
    resources: {}
    securityContext:
      allowPrivilegeEscalation: true
      capabilities: {}
      privileged: false
      readOnlyRootFilesystem: false
    volumeMounts:
    - mountPath: /var/opt/gitlab
      name: srv-podman-gitlab-data
    - mountPath: /var/log/gitlab
      name: srv-podman-gitlab-logs
    - mountPath: /etc/gitlab
      name: srv-podman-gitlab-conf
    workingDir: /
  - command:
    - run
    - --user=gitlab-runner
    - --working-directory=/home/gitlab-runner
    env:
    - name: PATH
      value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    - name: TERM
      value: xterm
    - name: HOSTNAME
    - name: container
      value: podman
    image: docker.io/gitlab/gitlab-runner:alpine
    name: rootgitlab-runner1
    resources: {}
    securityContext:
      allowPrivilegeEscalation: true
      capabilities: {}
      privileged: false
      readOnlyRootFilesystem: false
    volumeMounts:
    - mountPath: /etc/gitlab-runner
      name: srv-podman-gitlab-runner
    - mountPath: /var/run/docker.sock
      name: var-run-docker.sock
    workingDir: /
  volumes:
  - hostPath:
      path: /srv/podman/gitlab/runner
      type: Directory
    name: srv-podman-gitlab-runner
  - hostPath:
      path: /var/run/docker.sock
      type: File
    name: var-run-docker.sock
  - hostPath:
      path: /srv/podman/gitlab/data
      type: Directory
    name: srv-podman-gitlab-data
  - hostPath:
      path: /srv/podman/gitlab/logs
      type: Directory
    name: srv-podman-gitlab-logs
  - hostPath:
      path: /srv/podman/gitlab/conf
      type: Directory
    name: srv-podman-gitlab-conf
status: {}

Sistemuar:

# podman generate systemd 71fc2b2a5c63
# pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service
# autogenerated by Podman 1.6.4
# Thu Jul 29 15:23:28 EDT 2020

[Unit]
Description=Podman pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service
Documentation=man:podman-generate-systemd(1)
Requires=container-781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3.service container-da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864.service
Before=container-781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3.service container-da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864.service

[Service]
Restart=on-failure
ExecStart=/usr/bin/podman start db40ab8bf84bf35141159c26cb6e256b889c7a98c0418eee3c4aa683c14fccaa
ExecStop=/usr/bin/podman stop -t 10 db40ab8bf84bf35141159c26cb6e256b889c7a98c0418eee3c4aa683c14fccaa
KillMode=none
Type=forking
PIDFile=/var/run/containers/storage/overlay-containers/db40ab8bf84bf35141159c26cb6e256b889c7a98c0418eee3c4aa683c14fccaa/userdata/conmon.pid

[Install]
WantedBy=multi-user.target
# container-da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864.service
# autogenerated by Podman 1.6.4
# Thu Jul 29 15:23:28 EDT 2020

[Unit]
Description=Podman container-da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864.service
Documentation=man:podman-generate-systemd(1)
RefuseManualStart=yes
RefuseManualStop=yes
BindsTo=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service
After=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service

[Service]
Restart=on-failure
ExecStart=/usr/bin/podman start da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864
ExecStop=/usr/bin/podman stop -t 10 da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864
KillMode=none
Type=forking
PIDFile=/var/run/containers/storage/overlay-containers/da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864/userdata/conmon.pid

[Install]
WantedBy=multi-user.target
# container-781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3.service
# autogenerated by Podman 1.6.4
# Thu Jul 29 15:23:28 EDT 2020

[Unit]
Description=Podman container-781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3.service
Documentation=man:podman-generate-systemd(1)
RefuseManualStart=yes
RefuseManualStop=yes
BindsTo=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service
After=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service

[Service]
Restart=on-failure
ExecStart=/usr/bin/podman start 781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3
ExecStop=/usr/bin/podman stop -t 10 781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3
KillMode=none
Type=forking
PIDFile=/var/run/containers/storage/overlay-containers/781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3/userdata/conmon.pid

[Install]
WantedBy=multi-user.target

Fatkeqësisht, përveç lëshimit të kontejnerëve, njësia e gjeneruar për systemd nuk bën asgjë tjetër (për shembull, pastrimi i kontejnerëve të vjetër kur një shërbim i tillë riniset), kështu që do të duhet të shkruani vetë gjëra të tilla.

Në parim, Podman është i mjaftueshëm për të provuar se cilat janë kontejnerët, transferoni konfigurimet e vjetra për docker-compose dhe më pas lëvizni drejt Kubernetes, nëse keni nevojë për një grup, ose merrni një alternativë më të lehtë për t'u përdorur për Docker.

rkt

Projekt hyri në arkiva rreth gjashtë muaj më parë për faktin se RedHat e bleu, kështu që nuk do të ndalem më në detaje. Në përgjithësi, ka lënë një përshtypje shumë të mirë, por në krahasim me Docker dhe veçanërisht Podman, duket si një kombinim. Kishte gjithashtu një shpërndarje CoreOS të ndërtuar në krye të rkt (megjithëse ata fillimisht kishin Docker), por kjo gjithashtu përfundoi në mbështetje pas blerjes së RedHat.

Pelush

Më shumë një projekt, autori i të cilit thjesht donte të ndërtonte dhe drejtonte kontejnerë. Duke gjykuar nga dokumentacioni dhe kodi, autori nuk ndoqi standardet, por thjesht vendosi të shkruante zbatimin e tij, gjë që, në parim, e bëri.

Gjetjet

Situata me Kubernetes është shumë interesante: nga njëra anë, me Docker mund të ndërtoni një grup (në modalitetin swarm), me të cilin mund të ekzekutoni edhe mjedise produkti për klientët, kjo është veçanërisht e vërtetë për ekipet e vogla (3-5 persona) , ose me një ngarkesë të vogël të përgjithshme, ose mungesë dëshire për të kuptuar ndërlikimet e konfigurimit të Kubernetes, duke përfshirë edhe ngarkesat e larta.

Podman nuk ofron pajtueshmëri të plotë, por ka një avantazh të rëndësishëm - pajtueshmërinë me Kubernetes, duke përfshirë mjete shtesë (buildah dhe të tjerët). Prandaj, unë do t'i qasem zgjedhjes së një mjeti për punë si më poshtë: për ekipe të vogla, ose me një buxhet të kufizuar - Docker (me një modalitet të mundshëm tufë), për t'u zhvilluar për veten time në një host personal lokal - shokët Podman, dhe për të gjithë të tjerët - Kubernetes.

Nuk jam i sigurt se situata me Docker nuk do të ndryshojë në të ardhmen, në fund të fundit, ata janë pionierë, dhe gjithashtu gradualisht po standardizohen hap pas hapi, por Podman, me të gjitha mangësitë e tij (punon vetëm në Linux, pa grumbullim, asambleja dhe veprimet e tjera janë zgjidhje të palëve të treta) e ardhmja është më e qartë, ndaj i ftoj të gjithë t'i diskutojnë këto gjetje në komente.

PS Më 3 gusht nisim “Kursi video Docker“, ku mund të mësoni më shumë për punën e tij. Ne do të analizojmë të gjitha mjetet e tij: nga abstraksionet bazë deri te parametrat e rrjetit, nuancat e punës me sisteme të ndryshme operative dhe gjuhë programimi. Do të njiheni me teknologjinë dhe do të kuptoni se ku dhe si të përdorni më mirë Docker. Ne gjithashtu do të ndajmë rastet e praktikave më të mira.

Çmimi para-porosit para lëshimit: 5000 RUB. Ju mund të shikoni programin Docker Video Course në faqen e kursit.

Burimi: www.habr.com

Shto një koment