Инструменты пользователя

Инструменты сайта


service:docker

Docker

— What is the first clue to build the container?
— It must be bound in thews of invincible iron.
— What is the second clue to build the container?
— Bejewel the chest with the hallowed heart of glittering stone.
— What is the third clue to build the container?
— Gift it with the essence of dragon amber, born of earth and root.
— What is the fourth clue to build the container?
— A sacrifice of clay. The power to bind its parts.
— What is the fifth clue to build the container?
— Craft it from wood no mortal blade can carve. Find the wicked tree.
— What is the sixth clue to build the container?
— The strength of love denied. The soul of a dead hero shall empower it.
— What is the seventh clue to build the container?
— Temper it in the tears of the weeping moon.
— What is the eighth clue to build the container?
— Forged by the legendary Black Gnarl, beneath his binding song.
— Each of the hearts have I given.
— And each of the riddles have I returned. I am free! Stay at your task, my fool. No greater curse might I impart!

Anvil of Dawn

Обновление

# pull latest images
docker-compose pull
# restart containers
docker-compose up -d --remove-orphans
# remove obsolete images
docker image prune

https://stackoverflow.com/questions/49316462/how-to-update-existing-images-with-docker-compose

Чистка

/etc/crontab
### Docker cleaning
0 3     * * 2,4,6       root  docker container prune -f; docker image prune -af
0 3     * * 7           root  docker system prune -f

https://docs.docker.com/config/pruning/

Тома для постоянного хранения

sshfs

sshfs монтирует каталог с другого сервера через SSH.

### На сервере хранения:
# Создать каталог для тома (он должен быть, автоматически не создаётся):
mkdir -p /home/user/dockervol/bepasty
# Создать файл dump (иначе volume не подмонтируется, будет ошибка lchown permission denied, это проблема sshfs)
touch /home/user/dockervol/bepasty/dump
 
### На сервере докера:
# Установка дополнения sshfs
docker plugin install --grant-all-permissions vieux/sshfs

https://github.com/vieux/docker-volume-sshfs/issues/76

Пример с паролем, лучше через переменные или через ключи.

version: '3.9'

services:
  bepasty:
    build: .
    container_name: bepasty
    restart: unless-stopped
    volumes:
      - bepasty:/storage
    ports:
      - 80:5000

volumes:
  bepasty:
    name: "bepasty"
    driver: "vieux/sshfs:latest"
    driver_opts:
      sshcmd: "user@server1:/home/user/dockervol/bepasty"
      password: "P@ssw0rd"

https://docs.docker.com/storage/volumes/#share-data-between-machines

Резервное копирование тома

С помощью другого контейнера, который подключается к томам основного.

# Запуск 
docker run -d -v /dbdata --name dbstore alpine sleep infinity
# Создание файла
docker exec -it dbstore sh -c "echo 'Hello' >> /dbdata/file.txt"
# Проверка
docker exec -it dbstore sh -c "cat /dbdata/file.txt"
 
# Бэкап
docker run --rm --volumes-from dbstore -v $(pwd):/backup alpine tar cvf /backup/backup.tar /dbdata
 
# Запуск второго контейнера
docker run -d -v /dbdata --name dbstore2 alpine sleep infinity
# Восстановление из архива
docker run --rm --volumes-from dbstore2 -v $(pwd):/backup alpine sh -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1"
# Проверка
docker exec -it dbstore2 sh -c "cat /dbdata/file.txt"

https://docs.docker.com/storage/volumes/#back-up-restore-or-migrate-data-volumes

Мониторинг

Healthcheck

Можно делать в Dockerfile, можно в docker-compose.yml

Варианты строки проверки

curl --fail http://localhost:8080/health
# иногда curl-а нет в образе, тогда
wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1

https://docs.docker.com/engine/reference/builder/#healthcheck
https://docs.docker.com/compose/compose-file/#healthcheck
https://medium.com/geekculture/how-to-successfully-implement-a-healthcheck-in-docker-compose-efced60bc08e

Prometheus

/etc/prometheus/prometheus.yml

# my global config
global:
  scrape_interval:     15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).
 
# Alertmanager configuration
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      # - alertmanager:9093
 
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"
 
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'prometheus'
    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.
    static_configs:
    - targets: ['localhost:9090']

  - job_name: 'docker'
    static_configs:
      - targets: ['172.17.0.1:9323']

  - job_name: 'traefik'
    static_configs:
      - targets: ['traefik:8080']

172.17.0.1 = host.docker.internal
If you're running with –net=host, localhost should work fine.
If you're using default networking, use the static IP 172.17.0.1. I suspect neither will behave quite the same as those domains.
https://stackoverflow.com/questions/48546124/what-is-linux-equivalent-of-host-docker-internal

Dozzle

# На удалённом хосте запустить докер-прокси, чтобы можно было подключиться удалённо
docker container run --rm --name dockerproxy -e CONTAINERS=1 -v /var/run/docker.sock:/var/run/docker.sock -p 2375:2375 tecnativa/docker-socket-proxy
# Запустить Dozzle (будет мониториться как локальный сервер через сокет, так и удалённый через прокси)
docker run --rm --name dozzle -p 9999:8080 -v /var/run/docker.sock:/var/run/docker.sock amir20/dozzle:latest --base /logs --remote-host tcp://192.168.1.8:2375

https://github.com/amir20/dozzle
https://dozzle.dev/guide/remote-hosts
https://github.com/Tecnativa/docker-socket-proxy

Докер-прокси:

docker-compose.yml
version: '3.7'

services:
  docker-proxy:
    image: tecnativa/docker-socket-proxy
    container_name: docker-proxy
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      CONTAINERS: "1"
    ports:
      - 2375:2375

Dozzle (на другом сервере, локальный Докер не мониторится)

docker-compose.yml
version: '3.7'

services:
  dozzle:
    image: amir20/dozzle
    container_name: dozzle
    restart: unless-stopped
    environment:
      DOZZLE_BASE: "/dockerlogs"
      DOZZLE_REMOTE_HOST: "tcp://server1:2375,tcp://server2:2375"
    ports:
      - 8080:8080

Деплой

Blue-green

#!/bin/bash
 
replicas=2
 
function deploybg {
  docker-compose rm -sf $2
  docker-compose pull $2
  docker-compose up -d --scale $2=$replicas --force-recreate $2
  until [[ ${#count[@]} = $replicas ]]; do
    count=( $(docker ps -qf name=$2 -f health=healthy) )
    sleep 10
  done
  docker-compose rm -sf $1
}
 
if [[ $(docker ps -qf name=backend-blue) ]]
then
  deploybg backend-blue backend-green
else
  deploybg backend-green backend-blue
fi

В Gitlab (1 реплика и удалённый контекст, т. к. непонятно, как экранировать #):

script:
    - docker context create remote --docker "host=ssh://${USER}@${HOST}"
    - docker context use remote
    - |
      function deploybg {      
        docker-compose --context remote rm -sf $2
        docker-compose --context remote pull $2
        docker-compose --context remote up -d --force-recreate $2
        until [[ $(docker ps -qf name=$2 -f health=healthy) ]]
        do
          sleep 10
        done
        docker-compose --context remote rm -sf $1
      }
      if [[ $(docker ps -qf name=backend-blue) ]]
      then
        deploybg backend-blue backend-green
      else
        deploybg backend-green backend-blue
      fi 

Хранение данных

Решение проблем

Cannot start service <servicename>: get <id>: no such volume

Starting bepasty ... error
ERROR: for bepasty  Cannot start service bepasty: get 441a8f149da5cbd4fe25591303b0d7885f085729819c5b8aef52dd22b25b1072: no such volume
ERROR: for bepasty  Cannot start service bepasty: get 441a8f149da5cbd4fe25591303b0d7885f085729819c5b8aef52dd22b25b1072: no such volume
ERROR: Encountered errors while bringing up the project.

Решение:

docker container prune
docker-compose up -d

cannot stop container: permission denied

Невозможно остановить/удалить/пересоздать контейнеры, ошибки такие:

Error response from daemon: cannot stop container: c2a63f33d8b5: permission denied
Error response from daemon: Cannot kill container: c2a63f33d8b5: permission denied

Перезапуск ОС/Докера ничего не даёт

Решение: It turned out that AppArmor service was messing up with Docker. AppArmor (or “Application Armor”) is a Linux kernel security module that allows the system administrator to restrict programs’ capabilities with per-program profiles. For this problem with containers, it helped me to remove the unknown from AppArmor using the following command:

sudo aa-remove-unknown

After that, I was able to stop and kill my containers.

https://debugah.com/solved-cannot-kill-docker-container-permission-denied-23902/

Запустить команду с конвейером (pipe) в контейнере с хоста

# Use "docker exec" to change user password in container from host.
docker exec ops_ubuntu_ntp2 echo "cobra_demo:cobra_pw"|chpasswd
# Fail to user chpasswd to change password of user cobra_demo in container.
# This is because the Pipe (|).
# The command chpasswd is executed in host not in container. 
# However, host accept the output from docker container as it parameter.
 
docker exec ops_ubuntu_ntp2 echo "cobra_demo:cobra_pw"
#try another way, use "echo" to pass parameter to "docker exec"
echo "cobra_demo:cobra_pw"|chpasswd | docker exec ops_ubuntu_ntp2 /bin/bash/
 
# I tried many solution and found the following command works
# The -i parameter is must.
echo "echo cobra_demo:cobra_pw|chpasswd" | docker exec ops_ubuntu_ntp2 /bin/bash
 
# Paramater -i for docker exec. I don't know why, but it works.
echo "echo cobra_demo:cobra_pw|chpasswd" | docker exec -i ops_ubuntu_ntp2 /bin/bash

https://www.youtube.com/watch?v=6fEmryhIbr4 (текст в описании)

Блокировка dockerhub.io в России

С 30 мая 2024 г.

1. Конфиг докера (как зеркало docker.io)

операционная система путь к файлу конфигурации
Linux, regular setup /etc/docker/daemon.json
Linux, rootless mode ~/.config/docker/daemon.json
Windows C:\ProgramData\docker\config\daemon.json
cat << EOF |sudo tee -a /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://mirror.gcr.io",
    "https://dockerhub.timeweb.cloud",
    "https://daocloud.io",
    "https://c.163.com",
    "https://huecker.io",
    "https://registry.docker-cn.com"
  ]
}
EOF

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

2. Явное указание адреса

docker pull mirror.gcr.io/library/alpine:latest

https://huecker.io/
https://habr.com/ru/news/818177/comments/

docker.errors.DockerException: Error while fetching server API version

На новых системах после установки docker-compose и попыткой им воспользоваться выходит ошибка
docker.errors.DockerException: Error while fetching server API version: HTTPConnection.request() got an unexpected keyword argument ‘chunked’

Решение: снести старый docker-compose и установить docker-compose-v2.

sudo apt remove docker-compose -y
sudo apt install docker-compose-v2 -y

После этого команды выглядят так:

# без дефиса
docker compose up -d
docker compose ps
# и т. д.

Полезное

Запуск контейнера не от root

  1. Don’t run Docker container processes as root. Santa won’t bring you any gifts if you use root.
  2. Use su-exec, gosu, chroot, or setpriv to step down from root into another user inside your Docker containers.
  3. You can use Supervisor to manage processes in Docker containers and step down from root. But unless you have a very specific use case to do so, you shouldn’t because it’s at least 50MB heavier.

su-exec is a new-ish alternative to gosu. Both provide similar functionality, but su-exec weights only 10kb instead of 1.8MB.

This shows how to:

  1. Install php7-fpm and su-exec on an Alpine 3.11 Docker image;
  2. Step down from user root into user nobody using su-exec, and;
  3. Execute php-fpm using su-exec:
FROM alpine:3.11
RUN apk add --no-cache php7-fpm su-exec
ENTRYPOINT ["su-exec", "nobody", "/usr/sbin/php-fpm7", "--nodaemonize", "--force-stderr"]

Docker Swarm - запуск команды в контейнере сервиса

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

docker exec $(docker ps -q -f name='lk_frontend_nginx' |head -n 1) ls

Права

В контейнере PHP и Nginx есть юзер www-data (ID 33), которого можно использовать вместо рута при совместной работе.

Dockerfile
FROM composer AS vendor
WORKDIR /app
 
COPY composer.json .
RUN composer install --ignore-platform-reqs --no-dev --no-scripts
 
 
FROM php:8.0-fpm
COPY --chown=33:33 --from=vendor /app/vendor ./vendor
 
RUN \
apt update && apt install -y --no-install-recommends \
libzip-dev \
libpng-dev \
libxml2-dev && \
docker-php-ext-install \
pdo_mysql \
exif \
pcntl \
bcmath \
gd \
soap \
zip \
sockets
 
USER 33

То же самое, только размер в 5 раз меньше

Dockerfile
FROM composer AS vendor
WORKDIR /app
 
COPY composer.json .
RUN composer install --ignore-platform-reqs --no-ansi --no-dev --no-interaction --no-progress --no-scripts --optimize-autoloader
 
FROM alpine:3.16
WORKDIR /var/www/html
 
RUN apk add --no-cache \
php8 \
php8-fpm \
php8-mbstring \
php8-pdo_mysql \
php8-exif \
php8-pcntl \
php8-bcmath \
php8-gd \
php8-soap \
php8-zip \
php8-sockets \
php8-session \
php8-fileinfo && \
addgroup -g 82 -S www-data || true && \
adduser -u 82 -D -S -G www-data www-data || true && \
sed -i '/listen = /c listen = 0.0.0.0:9000' /etc/php8/php-fpm.d/www.conf && \
chown -R 82: /var/log/php*
#sed -i '/user =/c user = www-data' /etc/php8/php-fpm.d/www.conf && \
#sed -i '/group =/c group = www-data' /etc/php8/php-fpm.d/www.conf && \
#sed -i '/php_admin_value\[memory_limit\] = /c php_admin_value[memory_limit] = 512M' /etc/php8/php-fpm.d/www.conf && \
#sed -i '/memory_limit = /c memory_limit = 512M' /etc/php8/php.ini && \
#sed -i 's/;clear_env/clear_env/' /etc/php8/php-fpm.d/www.conf && \
#sed -i 's/^;env/env/' /etc/php8/php-fpm.d/www.conf
 
COPY --chown=82:82 --from=vendor /app/vendor ./vendor
COPY --chown=82:82 . .
 
EXPOSE 9000
#websockets
#EXPOSE 6001
 
USER 82
 
#RUN php artisan key:generate
 
CMD ["php-fpm8", "-F"]

https://hub.docker.com/_/composer
https://hub.docker.com/_/php
http://www.inanzzz.com/index.php/post/k4r7/using-docker-multi-stage-builds-to-run-composer-install-and-copy-application-into-php-container-image
https://dev.to/mtk3d/read-this-before-you-start-using-the-multistage-builds-for-your-docker-images-21e7
https://laravel-news.com/multi-stage-docker-builds-for-laravel
https://nsirap.com/posts/039-docker-best-practice-multi-stage-build/

В стандартных томах владелец меняется на папке _data, т. е.

docker run --rm -it -v alpinetest:/alpinetest alpine:3.16 sh
chown -R 82: /alpinetest
# На хосте:
ls -l /var/lib/docker/volumes
drwx-----x 3 root root   4.0K Jun 13 11:07 alpinetest
ls -l /var/lib/docker/volumes/alpinetest
drwxr-xr-x 2 82 82 4.0K Jun 13 11:09 _data

nginx

Шаблон конфигурации - берёт переменные окружения и поставляет их в шаблон, а потом пишет как файл конфигурации, убирая .template из имени файла.
Стандартный путь к шаблонам: /etc/nginx/templates/
Стандартный путь к конфигурациям: /etc/nginx/conf.d/

default.conf.template
server {
    listen 80;
    index index.php index.html;
    root ${NGINX_ROOT};
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass ${NGINX_FASTCGI_PASS}:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }
}
Dockerfile
FROM nginx:alpine
 
COPY default.conf.template /etc/nginx/templates/
docker-compose.yml
version: "3.7"
service:
  nginx:
    build: ./nginx
    container_name: nginx
    environment:
      NGINX_ROOT: /var/www/html/public
      NGINX_FASTCGI_PASS: app
    volumes_from:
      - app
    ports:
      - 80:80

https://github.com/docker-library/docs/tree/master/nginx#using-environment-variables-in-nginx-configuration

PHP

В alpine:3.18 - версии 8.1 и 8.2
В alpine:3.17 - версия 8.1
В alpine:3.16 - версии 8 и 8.1 (по умолчанию apk add php ставится 8)

How to configure PHP logs for Docker

php-fpm healthcheck

Laravel

nodejs

Сборка в каталоге, лежащем на хосте

docker run --rm -w /home/node -v ./source:/home/node node:16 npm run build

Сложный и неэффективный вариант (пока не понял, что можно проще).
Интересный приём - heredoc для docker build.

workdir=/home/node
imagename=front:temp
 
docker build -t $imagename -f - . << EOF
FROM node:16
WORKDIR $workdir
COPY --chown=node:node source/ ./
USER node
# npm rebuild нужен для решения проблемы с правами cross-env: Permission denied
RUN npm rebuild && npm run build
EOF
 
# Вытащить каталоги client и admin из образа с архивацией gzip
id=$(docker create $imagename)
for i in client admin
do
  docker cp $id:$workdir/$i - |gzip > result/$i.tar.gz
done
docker rm $id

Скачивание и пересылка образа в закрытый контур

В закрытом контуре интернета нет, поэтому на какой-то внешней машине:

docker pull tomcat:jre11
docker save tomcat:jre11 |gzip > tomcat_jre11.tar.gz
# Куски по 24 МБ, цифровые индексы, в конец добавить .tar.gz, входной файл, имя (префикс) выходных файлов
split -b 24M -d --additional-suffix=.tar.gz tomcat_jre11.tar.gz tomcat_jre11_
ll -h
-rw-rw-r--  1 user user  24M May 21 11:53 tomcat_jre11_00.tar.gz
-rw-rw-r--  1 user user  24M May 21 11:53 tomcat_jre11_01.tar.gz
-rw-rw-r--  1 user user  24M May 21 11:53 tomcat_jre11_02.tar.gz
-rw-rw-r--  1 user user  24M May 21 11:53 tomcat_jre11_03.tar.gz
-rw-rw-r--  1 user user 1.5M May 21 11:53 tomcat_jre11_04.tar.gz
-rw-rw-r--  1 user user  98M May 21 11:44 tomcat_jre11.tar.gz

Дальше вытаскивать архивы наружу и слать по почте вложением или ещё как-нибудь.
Выгрузка образов: https://docs.docker.com/reference/cli/docker/image/save/

На целевом сервере: объединение частей и загрузка образа.

cat tomcat_jre11_* > tomcat_jre11.tar.gz
zcat tomcat_jre11.tar.gz |docker load

Сохранение всех образов в архивы

backupdir=/tmp/docker_images_backup
 
mkdir $backupdir
# Кроме образов с тэгами, начинающимися на host:port/, т. е., тэгированных для частных репозиториев
images=($(docker image ls | awk '!/REPOSITORY|<none>/ {print $1":"$2}' |egrep -v ^.*:[[:digit:]]*/))
#  Замена / на _ в именах архивов
for i in "${images[@]}" ; do docker save $i |gzip > $backupdir/${i//\//_}.tar.gz ; done
user@k3:~/dockerimages$ ll -h
total 3.6G
drwxrwxr-x  2 user user 4.0K May 31 14:40 ./
drwxr-x--- 23 user user 4.0K May 31 14:26 ../
-rw-rw-r--  1 user user  11M May 31 14:37 epoupon_fileshelter:latest.tar.gz
-rw-rw-r--  1 user user  19M May 31 14:37 f0rc3_gokapi:latest.tar.gz
-rw-rw-r--  1 user user 636M May 31 14:36 frooodle_s-pdf:latest.tar.gz
-rw-rw-r--  1 user user 330M May 31 14:39 gradle:7.3.3-jdk11-alpine.tar.gz
-rw-rw-r--  1 user user 286M May 31 14:41 maven:3.5.2.tar.gz
-rw-rw-r--  1 user user  53M May 31 14:35 minio_minio:latest.tar.gz
-rw-rw-r--  1 user user 143M May 31 14:34 mirror.gcr.io_library_postgres:latest.tar.gz
-rw-rw-r--  1 user user  18M May 31 14:37 mtlynch_picoshare:latest.tar.gz
-rw-rw-r--  1 user user 324M May 31 14:33 nexus_nexus:latest.tar.gz
-rw-rw-r--  1 user user 487M May 31 14:40 nikosch86_onionshare:latest.tar.gz
-rw-rw-r--  1 user user 316M May 31 14:38 openjdk:11.tar.gz
-rw-rw-r--  1 user user 182M May 31 14:39 openjdk:17-alpine.tar.gz
-rw-rw-r--  1 user user 129M May 31 14:37 postgres:15.0.tar.gz
-rw-rw-r--  1 user user  75M May 31 14:36 psitrax_psitransfer:latest.tar.gz
-rw-rw-r--  1 user user 324M May 31 14:34 sonatype_nexus3:latest.tar.gz
-rw-rw-r--  1 user user 137M May 31 14:37 stonith404_pingvin-share:latest.tar.gz
-rw-rw-r--  1 user user  98M May 31 14:35 tomcat:9-jre11.tar.gz
-rw-rw-r--  1 user user  98M May 31 14:34 tomcat:jre11.tar.gz

Загрузка всех образов из архивов

for i in $(ls *.tar.gz); do zcat $i |docker load; done

Убрать повторяющиеся куски кода из docker-compose.yml

Называется «compose file extensions». Пишется код, ему присваивается якорь, а потом на него идёт ссылка.
version: должна быть 3.4 и новее. Механизм негибкий, дочернюю строку в алиас не добавишь.

version: '3.7'

x-fluentd: &fluentd
  driver: fluentd
  options: &fluentdopts
    fluentd-async-connect: 1

services:
  nodeinfo:
    image: nodeinfo
    logging:
      <<: *fluentd
      options:
        <<: *fluentdopts
        tag: "nodeinfo"

https://docs.docker.com/compose/compose-file/11-extension/
https://stackoverflow.com/questions/39731125/can-i-concatenate-aliases-in-yaml

Сделать сеть с другим именем, не прописывая её явно для всех сервисов

Все сервисы стандартно ссылаются на сеть default, а тут ей задано другое имя. В результате будет сеть elastic, но прописывать её в сервисы не надо.

networks:
  default:
    name: elastic
    external: false

https://github.com/elkninja/elastic-stack-docker-part-one/blob/main/docker-compose.yml

service/docker.txt · Последнее изменение: 01.12.2024 13:15 — viacheslav

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki