Первый шаг

давайте представим, что у нас есть один узел nginx и один узел php, и мы хотим развернуть узел на узле php без простоев.
Наша структура такая

.
├── app
│   └── index.php
├── docker-compose.yml
├── nginx.Dockerfile
├── nginx.conf
└── php.Dockerfile
Войти в полноэкранный режим

Выйти из полноэкранного режима

Это наш docker-compose.yml

version: "3.7"

services:
    cicd-nginx:
        build:
          context: .
          dockerfile: nginx.Dockerfile
        ports:
            - "88:80"
        volumes:
            - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro

    cicd-php:
        build:
          context: .
          dockerfile: php.Dockerfile
Войти в полноэкранный режим

Выйти из полноэкранного режима

Это наш nginx.Dockerfile

FROM nginx:1.17-alpine

WORKDIR /app
COPY ./app ./
Войти в полноэкранный режим

Выйти из полноэкранного режима

Это наш nginx.conf

server {
    listen 80;
    index index.php index.html;

    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;

    root /app;
    server_name localhost;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass cicd-php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;

    }
}
Войти в полноэкранный режим

Выйти из полноэкранного режима

Это наш простой php.Dockerfile

FROM php:fpm
RUN docker-php-ext-install pdo pdo_mysql mysqli

RUN curl -sS  | \
    php -- --install-dir=/usr/bin/ --filename=composer

RUN apt-get update && apt-get install -y \
    git \
    unzip

RUN apt-get update -y && apt-get install -y sendmail libpng-dev
RUN docker-php-ext-install gd

WORKDIR /app
COPY ./app ./
Войти в полноэкранный режим

Выйти из полноэкранного режима

И в конце наш простой index.php

<?php

echo "hi"

?>
Войти в полноэкранный режим

Выйти из полноэкранного режима


Переходим к следующей интересной части

Описание изображения

Сначала мы меняем build с image потому что мы хотим сначала создать образ, а затем просто заменить новый старым, поэтому наш docker-compose.yml станет таким:

version: "3.7"

services:
    cicd-nginx:
        image: ${NGINX_IMAGE_TAG}
        ports:
            - "88:80"
        volumes:
            - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro

    cicd-php:
        image: ${PHP_IMAGE_TAG}
Войти в полноэкранный режим

Выйти из полноэкранного режима

Сначала установите переменные и необходимость построения изображений

export PHP_IMAGE_TAG=phptest:1
export NGINX_IMAGE_TAG=nginxtest:1

docker build -f php.Dockerfile -t $PHP_IMAGE_TAG .
docker build -f nginx.Dockerfile -t $NGINX_IMAGE_TAG .
Войти в полноэкранный режим

Выйти из полноэкранного режима

Тогда мы просто бежим up -d


Теперь я хочу показать вам, что произойдет, если вы просто захотите развернуть новый код.

Сначала подготовим для него bash-скрипт (./script.sh):

#!/bin/bash
while :
do
    echo "Your response : "
    curl 127.0.0.1:88
    echo " "
    date
    sleep 2
done
Войти в полноэкранный режим

Выйти из полноэкранного режима

Теперь мы просто меняем файл php на что-то вроде

<?php

echo "hi hi :)"

?>
Войти в полноэкранный режим

Выйти из полноэкранного режима

И затем мы пытаемся построить его с новым именем

export PHP_IMAGE_TAG=phptest:2
docker build -f php.Dockerfile -t $PHP_IMAGE_TAG .
Войти в полноэкранный режим

Выйти из полноэкранного режима

Теперь мы хотим запустить скрипт, а затем запустить эти команды для обновления контейнера php.

sh ./script.sh
Войти в полноэкранный режим

Выйти из полноэкранного режима

А также

docker-compose down
docker-compose up -d
Войти в полноэкранный режим

Выйти из полноэкранного режима


Результат

Вы увидите что-то вроде этого, и это проблема

...
Your response : 
hi 
Mon Oct 31 00:31:23 UTC 2022
Your response : 
hi 
Mon Oct 31 00:31:25 UTC 2022
Your response : 
hi 
Mon Oct 31 00:31:27 UTC 2022
Your response : 
hi 
Mon Oct 31 00:31:30 UTC 2022
Your response : 
curl: (7) Failed to connect to localhost port 88: Connection refused

Mon Oct 31 00:31:32 UTC 2022
Your response : 
curl: (7) Failed to connect to localhost port 88: Connection refused

Mon Oct 31 00:31:34 UTC 2022
Your response : 
curl: (7) Failed to connect to localhost port 88: Connection refused

Mon Oct 31 00:31:36 UTC 2022
Your response : 
curl: (7) Failed to connect to localhost port 88: Connection refused

Mon Oct 31 00:31:38 UTC 2022
Your response : 
<html>
<head><title>502 Bad Gateway</title></head>
<body>
<center><h1>502 Bad Gateway</h1></center>
<hr><center>nginx/1.17.10</center>
</body>
</html>

Mon Oct 31 00:31:40 UTC 2022
Your response : 
hi hi :) 
Mon Oct 31 00:31:42 UTC 2022
Your response : 
hi hi :) 
Mon Oct 31 00:31:44 UTC 2022
Your response : 
hi hi :) 
Mon Oct 31 00:31:46 UTC 2022
Your response : 
hi hi :) 
Mon Oct 31 00:31:48 UTC 2022
...
Войти в полноэкранный режим

Выйти из полноэкранного режима


Вы можете почувствовать, в чем проблема прямо сейчас, поэтому давайте попробуем ее исправить.

Описание изображения


Мы хотим использовать Docker Swarm

Сначала запустите его

docker swarm init
Войти в полноэкранный режим

Выйти из полноэкранного режима

Затем обновите файл docker-compose.yml следующим образом:

version: "3.7"

services:
    cicd-nginx:
        image: ${NGINX_IMAGE_TAG}
        ports:
            - "88:80"
        volumes:
            - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
        deploy:
          mode: replicated
          replicas: 2
          update_config:
            order: start-first
            failure_action: rollback
            delay: 5s

    cicd-php:
        image: ${PHP_IMAGE_TAG}
        deploy:
          mode: replicated
          replicas: 2
          update_config:
            order: start-first
            failure_action: rollback
            delay: 5s
Войти в полноэкранный режим

Выйти из полноэкранного режима

Затем запустите эту команду, и теперь вы готовы

docker stack deploy -c docker-compose.yml <stack_name>
Войти в полноэкранный режим

Выйти из полноэкранного режима

Смотрим на контейнеры

CONTAINER ID   IMAGE                   COMMAND                  CREATED              STATUS              PORTS                                       NAMES
6cc2b3516a3a   phptest:2               "docker-php-entrypoi…"   About a minute ago   Up About a minute   9000/tcp                                    website_cicd-php.1.ijd0lpj995318hpffx6gx5t44
7cffa4115598   phptest:2               "docker-php-entrypoi…"   About a minute ago   Up About a minute   9000/tcp                                    website_cicd-php.2.jbd2sg2qm6ounji76q99fk2ql
fcedfb42b44a   nginxtest:1             "nginx -g 'daemon of…"   About a minute ago   Up About a minute   80/tcp                                      website_cicd-nginx.2.8z4spuco9rtdxcpc8y0fux79p
99e1642cf461   nginxtest:1             "nginx -g 'daemon of…"   About a minute ago   Up About a minute   80/tcp                                      website_cicd-nginx.1.eo6erxose7asze8ref2poracc
Войти в полноэкранный режим

Выйти из полноэкранного режима

У нас есть 2 узла php и 2 узла nginx 🙂
Обновим узел php и посмотрим результат
Измените файл php и добавьте один hi

<?php

echo "hi hi hi :)"

?>
Войти в полноэкранный режим

Выйти из полноэкранного режима

И затем мы пытаемся создать php-узел с новым именем.

export PHP_IMAGE_TAG=phptest:3
docker build -f php.Dockerfile -t $PHP_IMAGE_TAG .
Войти в полноэкранный режим

Выйти из полноэкранного режима

Теперь мы хотим запустить скрипт, а затем выполнить команду для обновления контейнера php.

sh ./script.sh
Войти в полноэкранный режим

Выйти из полноэкранного режима

А также

docker stack deploy -c docker-compose.yml <stack_name>
Войти в полноэкранный режим

Выйти из полноэкранного режима


Результат

...
Your response : 
hi hi :) 
Mon Oct 31 00:53:53 UTC 2022
Your response : 
hi hi :) 
Mon Oct 31 00:53:55 UTC 2022
Your response : 
hi hi :) 
Mon Oct 31 00:53:57 UTC 2022
Your response : 
hi hi :) 
Mon Oct 31 00:53:59 UTC 2022
Your response : 
hi hi :) 
Mon Oct 31 00:54:01 UTC 2022
Your response : 
hi hi hi:) 
Mon Oct 31 00:54:03 UTC 2022
Your response : 
hi hi :) 
Mon Oct 31 00:54:05 UTC 2022
Your response : 
hi hi hi:) 
Mon Oct 31 00:54:07 UTC 2022
Your response : 
hi hi :) 
Mon Oct 31 00:54:09 UTC 2022
Your response : 
hi hi hi:) 
Mon Oct 31 00:54:11 UTC 2022
Your response : 
hi hi hi:) 
Mon Oct 31 00:54:13 UTC 2022
Your response : 
hi hi hi:) 
Mon Oct 31 00:54:15 UTC 2022
Your response : 
hi hi hi:) 
Mon Oct 31 00:54:17 UTC 2022
Your response : 
hi hi hi:) 
Mon Oct 31 00:54:19 UTC 2022
Your response : 
hi hi hi:) 
Mon Oct 31 00:54:21 UTC 2022
Your response : 
hi hi hi:) 
Mon Oct 31 00:54:23 UTC 2022
Your response : 
hi hi hi:) 
Mon Oct 31 00:54:25 UTC 2022
...
Войти в полноэкранный режим

Выйти из полноэкранного режима

Вау и это волшебство
У нас нет простоев, и Docker Swarm полностью справится с этим за нас.


давайте сделаем для него .gitlab-ci.yml

Шаг первый создайте проект в gitlab
Шаг второй: добавьте проект в git, а затем нажмите его

git init
git add .
git commit -m "init"
git remote add origin git@gitlab.com:azibom/cicd.git
Войти в полноэкранный режим

Выйти из полноэкранного режима

Шаг третий добавить .gitlab-ci.yml к проекту

image: docker:20.10.16

stages:
  - publish
  - deploy

variables:
  PHP_TAG_COMMIT: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:php-$CI_COMMIT_SHORT_SHA
  NGINX_TAG_COMMIT: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:nginx-$CI_COMMIT_SHORT_SHA
  DOCKER_TLS_CERTDIR: "/certs"

services:
  - docker:20.10.16-dind

before_script:
  - 'command -v ssh-agent >/dev/null || ( apk add --update openssh )' 
  - eval $(ssh-agent -s)
  - echo "${SSH_PRIVATE_KEY}" | tr -d '\r' | ssh-add -
  - mkdir -p ~/.ssh
  - chmod 700 ~/.ssh

publish:
  stage: publish
  script:
    - docker build -f php.Dockerfile -t $PHP_TAG_COMMIT .
    - docker build -f nginx.Dockerfile -t $NGINX_TAG_COMMIT .
    - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY
    - docker push $PHP_TAG_COMMIT
    - docker push $NGINX_TAG_COMMIT

deploy:
  image: alpine:latest
  stage: deploy
  script:
    - ssh -o StrictHostKeyChecking=no ubuntu@130.185.120.185 "cd /opt/cicd/docker-compose-lemp ; docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY ;echo $NGINX_TAG_COMMIT; docker pull $NGINX_TAG_COMMIT"
    - ssh -o StrictHostKeyChecking=no ubuntu@130.185.120.185 "cd /opt/cicd/docker-compose-lemp && docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY && docker pull $PHP_TAG_COMMIT"
    - ssh -o StrictHostKeyChecking=no ubuntu@130.185.120.185 "cd /opt/cicd/docker-compose-lemp && export PHP_IMAGE_TAG=$PHP_TAG_COMMIT && export NGINX_IMAGE_TAG=$NGINX_TAG_COMMIT && docker stack deploy -c docker-compose.yml website"
  only:
    - master

Войти в полноэкранный режим

Выйти из полноэкранного режима

Шаг четвертый: добавьте переменную в gitlab ci из настройки

SSH_PRIVATE_KEY
Войти в полноэкранный режим

Выйти из полноэкранного режима

Ты можешь найти SSH_PRIVATE_KEY с запуском этой команды

cat ~/.ssh/id_rsa
Войти в полноэкранный режим

Выйти из полноэкранного режима

Также запустите это на сервере

cat ~/.ssh/id_rsa.pub > ~/.ssh/authorized_keys
Войти в полноэкранный режим

Выйти из полноэкранного режима

Шаг пятый: определите своего бегуна

curl -L  > script.deb.sh
sudo bash script.deb.sh
sudo apt install gitlab-runner
systemctl status gitlab-runner
sudo gitlab-runner register -n \
  --url  \
  --registration-token REGISTRATION_TOKEN \
  --executor docker \
  --description "My Docker Runner" \
  --docker-image "docker:20.10.16" \
  --docker-privileged \
  --docker-volumes "/certs/client"
Войти в полноэкранный режим

Выйти из полноэкранного режима

(принесите REGISTRATION_TOKEN с gitlab.com)
Шаг шестой: нажмите на свой сервер и посмотрите на конвейер