Docker

Установка. Docker часто меняет подход установки, поэтому действуем по официальной инструкции и выполняем следующую команду, чтобы добавить пользователя в группу:

sudo adduser $(whoami) docker && newgrp docker && source ~/.bashrc

docker -v

Docker version 1.9.1, build a34a1d5

docker version

Client:
Version: 1.9.1
API version: 1.21
Go version: go1.4.2
Git commit: a34a1d5
Built: Fri Nov 20 13:12:04 UTC 2015
OS/Arch: linux/amd64

Server:
Version: 1.9.1
API version: 1.21
Go version: go1.4.2
Git commit: a34a1d5
Built: Fri Nov 20 13:12:04 UTC 2015
OS/Arch: linux/amd64

docker info

Containers: 3
Images: 87
Server Version: 1.9.1
Storage Driver: aufs
Root Dir: /mnt/dsk1/docker/aufs
Backing Filesystem: extfs
Dirs: 93
Dirperm1 Supported: true
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 3.19.0-42-generic
Operating System: Ubuntu 14.04.3 LTS
CPUs: 8
Total Memory: 23.54 GiB
Name: workstation
ID: O6JG:4MOF:M526:3PJV:FQHZ:3ERJ:P7KW:U3VN:D6AZ:C46E:SSH3:IADV
Username: marley
Registry: https://index.docker.io/v1/
WARNING: No swap limit support

поискать в репо что-нибудь

docker search centos

взять из репо последнюю версию debian

docker pull debian

взять все версии debian

docker pull -a debian

получить список скачанных images

docker images
docker images --tree
docker images debian

Запустить контейнер и отправить 30 пингов до гугла

docker run -d ubuntu /bin/bash -c "ping 8.8.8.8 -c 30"

Запустить интерактивно контейнер и в контейнере shell

docker run -i -t centos:centos6 /bin/bash

-d - Detached mode (запустится в фоне)

docker run -i -t -d debian

Задать имя, иначе она будет выбрано самостоятельно

docker run -i -t -d --name myDebianServ debian

Запустить команду pwd в контейнере от текущего пользователя и затем удалить контейнер:

docker run --rm --user $(id -u):$(id -g) mysql:5.7 pwd

Монтирование

Примонтировать текущую директорию и посмотреть ее содержимое в контейнере:

docker run -v $(pwd):/tmp/build mysql:5.7 ls -la /tmp/build

Запустить и войти в контейнер, а при выходе удалить его: 

docker run -it --rm  -w /app -v $(pwd):/app -v $HOME/.ssh:/home/appuser/.ssh:ro -u appuser mysql:5.7 bash

Тома

Создание тома в памяти с именем foo и размером 100 мегабайт и uid размером 1000:

docker volume create --driver local \
    --opt type=tmpfs \
    --opt device=tmpfs \
    --opt o=size=100m,uid=1000 \
    foo

и подключение к контейнеру с значением 1770, чтобы том не был доступен для чтения внутри контейнера:

docker run -d \
  -it \
  --name tmptest \
  --mount type=tmpfs,destination=/app,tmpfs-mode=1770 \
  nginx:latest

Создание удаленной директории /home/test в качестве тома:

docker volume create --driver vieux/sshfs \
  -o sshcmd=test@node2:/home/test \
  -o password=testpassword \
  sshvolume

Запустить и войти в контейнер, при этом отключив энтрипоинт:

docker run -it --rm --entrypoint="" registry.site.ru/my-nginx:5195318305c sh

Удаление контейнера игнорируя код возврата:

# stop container elasticsearch (silently)
docker rm -f my_container_name || true

Контейнеры и образы хранятся здесь

cat /var/lib/docker/aufs/diff/<container_id>

ls -l /var/lib/docker/containers
ls -l /var/lib/docker/containers | wc -l

показать активные контейнеры

docker ps

показать все контейнеры в том числе остановленные

docker ps -a

Последний стартовавший контейнер.

docker ps -l

Удалить все контейнеры (не образы), которые сейчас не запущены:

docker container prune --force

Старт / стоп

docker start <container_id>
docker stop <container_id>
docker kill <container_id>
docker restart <container_id>

Изменение политики перезапуска контейнера (возможные значения):

docker update --restart=always -d your_image

Вот так можно отменить автозапуск редиса:

docker update --restart no redis

всего опций:

  • always
  • on-failure
  • unless-stopped
  • no

Сколько жрет ресурсов

docker stats <container_id>
docker top <container_id> -ef

Отключиться от контейнера docker без его остановки:

CTRL + P + Q

Подключиться

docker attach <container_id>

Подключиться еще одной сессией к контейнеру

docker exec -it <container_id> bash
docker top <container_id>
docker inspect <container_id>
docker logs <container_id>

Показать какие порты локальной машины соответствуют портам контейнера

docker port <container_id>

Пример

docker port my_container

1337/tcp -> 0.0.0.0:1337
3000/tcp -> 0.0.0.0:3000
8080/tcp -> 0.0.0.0:80
9000/tcp -> 0.0.0.0:9000

узнать IP контейнера Docker

docker inspect --format='' containerId

Остановка и удаление

Удалить контейнер

docker rm <container_id>
docker rm -f <container_id>

stop all Docker containers:

docker stop $(docker ps -a -q)

remove all Docker containers:

docker rm $(docker ps -a -q)

Удаляем образы, которые сейчас не запущены:

docker image prune --all --force

remove all Docker images:

docker rmi $(docker images -q)

удалить volumes:

docker system prune -af && docker image prune -af && docker system prune -af --volumes && docker system df

Получить информацию о слоях image

docker history <image_name>
docker history --no-trunc <image_name>

Возможно, более наглядно.

Сборка

Создать свой образ lebnik/ansible согласно файла ./docker/Dockerfile:

docker build -t lebnik/ansible ./docker

или с указанием пути к файлу:

docker build -t lebnik/ansible:latest -f ./my/Dockerfile ./my

где ./my это контекст который монтируется в Dockerfile (контекст не обязан содержать Dockerfile). Т.к. контекст это не примонтированная директория, то чтобы работать с директориями/файлами контекста в докер-образе, директорию нужно скопировать из контекста в докер-образ, например скопируем ./my/folder в /tmp/project:

ADD ./folder /tmp/project

или скопируем все содержимое директории ./my в директорию /app 

COPY . /app

Инструкции COPY и ADD одинаковы, но ADD умеет немного больше.

Копирование из какого-то образа в образ, который будем собирать:

# сначала объявляем, что при сборке будем использовать какой-то образ
FROM my_files_image as my_files
# последней FROM строкой объявляем на основании какого образа будем делать новый образ
FROM nginx:1.19.10-alpine
COPY --from=my_files /from/dir /to/dir

Если нужно прокинуть $SSH_AUTH_SOCK то всего лишь нужно добавить "--ssh default"

docker build --ssh default -t lebnik/ansible:latest -f ./my/Dockerfile ./my

насколько я понял, можно указать default=$SSH_AUTH_SOCK

Запустить и открыть порты локально

docker run -it --rm --net=host -v $SSH_AUTH_SOCK:/ssh-agent-sock --env SSH_AUTH_SOCK=/ssh-agent-sock my/img:latest bash

Проверить: а если при docker build указав --ssh default все равно не хватает прав, то в самом верху Dockerfile добавляют:

# syntax=docker/dockerfile:1

а при выполнении команды указывают:

RUN --mount=type=ssh set -xe && composer install

Подробности »

Сборка согласно цели

В докере можно задавать цели сборки и после собирать согласно указанной цели:

FROM nginx:1.19.6-alpine as prod

COPY ./docker-files/prod/nginx/nginx.conf /etc/nginx/nginx.conf

# перенаправление логов в потоки stdout,stderr
RUN ln -sf /dev/stdout /var/log/nginx/access.log && \
    ln -sf /dev/stderr /var/log/nginx/error.log

FROM prod as dev

# Prod nginx использует localhost чтобы обратиться к php-fpm, а docker-compose использует php-fpm поэтому так:
RUN sed -i 's|localhost|php-fpm|g' /etc/nginx/nginx.conf

Теперь сборка с указание цели:

docker build -t lebnik/nginx -f ./my/Dockerfile . --target prod

Однако, практика показала, что докер иногда игнорирует указание цели и выполняет все команды указанные в ./my/Dockerfile (делает больше чем мы просим). С чем это связано я пока не понял, но решил опцию --target пока не использовать.

Не помогает: DOCKER_BUILDKIT=1 docker build ... + нотация # syntax=docker/dockerfile:1 в первой строке ./my/Dockerfile

Монтирование других контейнеров

Иногда это нужно, например чтобы скопировать файлы из контейнера php-fpm в nginx:

ARG PHP_FPM_IMAGE=php7
FROM $PHP_FPM_IMAGE as php-fpm

FROM nginx:1.19.6-alpine

# как таковой сам файл index.php нам не важен, но нам важна статика в директории /app/public
COPY --from=php-fpm /app/public /app/public

Аргументы

Как видно в примере выше, был использовано указание ARG с значением по-умолчанию PHP_FPM_IMAGE, которое можно переопределить при сборке образа, например так:

docker build -t lebnik/nginx --build-arg "PHP_FPM_IMAGE=php8" .

IF на основе аргумента:

RUN case "$PHP_VERSION" in ( "8"* ) install-php-extensions xdebug;; ( * ) install-php-extensions xdebug-3.1.5;; esac

Логирование

По-умолчанию, docker использует драйвер логирования под названием: json-file Давайте узнаем путь файла, куда логируются данные

docker inspect logging-01 | grep LogPath

вывод:

"LogPath": "/var/lib/docker/containers/e5a8df3f41f0b308eb0ca057d6bc9e2d/e5a8df3f493bb94ed705cd7ea375655d4a53e-json.log",

А теперь можно настроить ротацию логов, получается путь такой:

/var/lib/docker/containers/*/*.log

p.s. а еще логи еще можно тегировать

Перенос образа

Пример:

docker build -t my-built/my-image:latest ./docker/php
docker save -o /tmp/my-image.tar my-built/my-image:latest
scp /tmp/my-image.tar my@remote-machine:/tmp
ssh my@remote-machine
docker load -i /tmp/my-image.tar

Маппинг имени сервиса

docker run --add-host="localhost:192.168.2.XX"

Сразу оговорюсь, из-за разных условий localhost всегда смотрит на 127.0.0.1, но др. имена прекрасно работают.

Как обратиться к IP-адресу машины, на которой запущен докер

docker run --add-host=host.docker.internal:host-gateway

Теперь если внутри контейнера обратиться к домену host.docker.internal то запрос уйдет на IP-адрес машины, на которой запущен докер (спасибо host-gateway).

Копирование файла из containerA в containerB

Конечно можно копировать с помощью монтирования директории и выполнения команды cp внутри контейнера, а можно так:

docker run --rm -d --name containerA php-8.1-fpm-alpine-dev:3.2.2
docker cp containerA:/usr/local/lib/php/extensions/no-debug-non-zts-20210902/xdebug.so xdebug.so
docker cp xdebug.so containerB:/usr/local/lib/php/extensions/no-debug-non-zts-20210902/xdebug.so
docker rm -f containerA

CMD vs ENTRYPOINT

CMD

Инструкция CMD позволяет вам установить команду по умолчанию , которая будет выполняться только при запуске контейнера без указания команды. Если контейнер Docker запускается с командой, команда по умолчанию будет проигнорирована. Если Dockerfile содержит более одной инструкции CMD, все инструкции CMD, кроме последней, игнорируются.

CMD имеет три формы:

  • CMD ["executable","param1","param2"] (форма exec, предпочтительно)
  • CMD ["param1","param2"](устанавливает дополнительные параметры по умолчанию для ENTRYPOINT в форме exec )
  • CMD command param1 param2 (форма оболочки)

Опять же, первая и третья формы были объяснены в разделе форм Shell и Exec . Второй используется вместе с инструкцией ENTRYPOINT в форме exec . Он устанавливает параметры по умолчанию, которые будут добавлены после параметров ENTRYPOINT, если контейнер запускается без аргументов командной строки. См. Например, ENTRYPOINT.

Давайте посмотрим, как работает инструкция CMD. Следующий фрагмент в Dockerfile

CMD echo "Hello world" 

когда контейнер запускается, как docker run -it <image>будет производить вывод

Hello world

но когда контейнер запускается с командой, например docker run -it <image> /bin/bash, CMD игнорируется и вместо этого запускается интерпретатор bash:

root@7de4bed89922:/#

ENTRYPOINT

Инструкция ENTRYPOINT позволяет вам настроить контейнер, который будет работать как исполняемый файл. Он похож на CMD, поскольку также позволяет указывать команду с параметрами. Разница в том, что команда ENTRYPOINT и параметры не игнорируются, когда контейнер Docker запускается с параметрами командной строки. (Есть способ игнорировать ENTTRYPOINT, но вряд ли вы это сделаете.)

ENTRYPOINT имеет две формы:

  • ENTRYPOINT ["executable", "param1", "param2"] (форма exec, предпочтительно)
  • ENTRYPOINT command param1 param2 (форма оболочки)

Будьте очень осторожны при выборе формы ENTRYPOINT, потому что поведение форм значительно отличается.

Форма Exec

Форма Exec ENTRYPOINT позволяет вам устанавливать команды и параметры, а затем использовать любую форму CMD для установки дополнительных параметров, которые с большей вероятностью будут изменены. Аргументы ENTRYPOINT используются всегда, в то время как аргументы CMD могут быть перезаписаны аргументами командной строки, предоставленными при запуске контейнера Docker. Например, следующий фрагмент в Dockerfile

ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["world"]

когда контейнер запускается, как docker run -it <image> будет производить вывод

Hello world

но когда контейнер запускается, docker run -it <image> John это приведет к

Hello John

Форма оболочки

Форма оболочки ENTRYPOINT игнорирует любые аргументы командной строки CMD или docker run.

Изменение существующего контейнера

docker run -d --name my_nginx nginx
docker exec my_nginx sed -i 's/localhost/my_host/' /etc/nginx/nginx.conf
docker commit -p --author lebnik my_nginx new_nginx
docker rm -f my_nginx || true
docker run -d --name new_nginx nginx

Сети

Запуск контейнера в собственной сети my-network-1 с собственным именем my-postgres:

docker network create my-network-1
docker run --rm -d --net=my-network-1 --net-alias=my-postgres \
   -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres postgres:9.6-alpine
# если надо, удалим сеть:
docker network rm my-network-1 || true

Подсети

Часто причиной проблем с сетью является конфликт диапазонов IP адресов, например, VPN подсеть пересекается с подсетью докера. Решается проблема изменением диапазона IP адресов для докера. Для этого нужно в конфиге демона докера /etc/docker/daemon.json указать следующее:

{
    "default-address-pools": [
        {"base": "10.10.0.0/16", "size": 24}
    ]
}

 

 

Источники: 1 - 2 - 3 - 4 - 5 - 6 - 7


20.06.2017 15:39