====== Контейнеры в Linux ====== ===== Docker ===== [[:linux:containers:docker-deep|Расширенная инфа по докеру]] ==== Установка docker ====
:!: Установка docker # apt install docker docker.io && systemctl enable docker # Установка на CentOS/Alma вместе с compose sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin curl -L "https://github.com/docker/compose/releases/download/v2.24.5/docker-compose-linux-x86_64" -o /usr/bin/docker-compose chmod +x /usr/bin/docker-compose && docker-compose --version # Скачать образ из репозитория: docker pull wallarm/node # Загрузить образ в репозиторий: docker push example.com:5000/my_image # Запустить свой registry можно всего одной командой: docker run -d -p 5000:5000 --restart=always --name registry registry:2
==== Работа с контейнерами ====
:!: Команды * **system** - системная информация * **df** - занимаемое место на диске * **prune** - очистить неиспользуемые данные * **events**, **info** * **images** - перечень локальных образов * **ps [-a]** || **container ls** - перечень работающих [всех] контейнеров * **top** - процессы внутри контейнера * **logs** - логи контейнера * * **stop** - "лояльная" остановка (остановить все можно так: docker stop $(docker container ls -q)) * **kill** - жесткая остановка * **rm** - удалить **контейнер** * **rmi** - удаление **образа** (с указанием имени и версии образа) * * **pull** - скачивание образа из репозитория (актуальную версию, сравнивает контрольную сумму) * **search** - поиск образов в репозитории * * **run** - запуск **образа** (создание контейнера) * **-i** - интерактивный режим * **-t** - поддержка эмулятора * **-d** - фоновый режим (отсоединенный) * **--name** - указание имени для создаваемого контейнера * ** ** - * **start** - запуск **контейнера** (уже созданного) * **exec** - доступ к работающему **контейнеру** * **ctrl+ q** - отсоединиться от работающего контейнера * **attach** - присоединиться к работающему контейнеру
:!: Инструкции Dockerfile Комментарии в файле с помощью #\\ | **FROM** | **Обязательно**, первая инструкция. Какой базовый образ нужно использовать | FROM ubuntu:16.04 | | **RUN** | Создает новый слой и выполняет команду. Родственная с CMD и ENTRYPOINT, во всех трех принимается как shell скрипт так и json синтаксис (в квадратных скобках) | RUN apt-get install python или RUN ["mkdir", "/my_dir"] | | **CMD** | Запускает команду с аргументами, каждый раз при запуске контейнера. Если указано несколько, то будет выполнена последняя | CMD ["openvpn"] | | **ENTRYPOINT** | Похожа на CMD, но ее пар-ры не переопределяются а добавляются из строки запуска контейнера | ENTRYPOINT ["/sbin/apache2"] | | **WORKDIR** | Рабочая директория для команд ENTRYPOINT,RUN,COPY,ADD (CMD ?) | WORKDIR /opt/apps | | **COPY** | Копирует файлы и деректории из рабочей папки в контейнер, создаст все пути | COPY ./mypassw/ /root/ | | **ADD** | Тоже самое но распакует архив либо скачает с URL, не рекоммендуется без необходимости этих ф-й | ADD https://site/video.mp4 /my_dir/ | | **VOLUME** | Добавляет том в контейнер для хранения постоянных данных | VOLUME ["/opt/myapp"] | | **USER** | Задает пользователя, от которого будет запущен образ | USER user:group | | **ENV** | Задает переменные окружения в образе | ENV PGPASSWD pass | | **ARG** | Создает переменную, которая доступна сборщику (в т.ч. в этом же файле). Фича - можно использовать для переопределения при билде образов | ARG folder=/opt/apps WORKDIR $folder | | **EXPOSE** | Указывает какой порт нужно пробросить из контейнера. Фактически не пробрасывает ничего, это форма документации для запускающего, для маппинга нужен аргумент -p при запуске (хост:конт). Хотя вроде используется в контейнерной сети, там доступен другим контейнерам | EXPOSE 80 | | **MAINTAINER** | Автор образа | MAINTAINER eNod | | **LABEL** | Добавляет информацию | LABEL version="2" | | **SHELL** | Позволяет заменить оболочку для выполнения команд на пользовательскую | SHELL ["/bin/sh", "-c"] | | **HEALTHCHECK** | Команда которая будет проверять работоспособность контейнера | HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost ll (верт черта) exit 1 | | **ONBUILD** | Действия, которые выполняются, если наш образ используется как базовый для другой сборки | ONBUILD ADD ./app/src | | **STOPSIGNAL** | Переопределяет сигнал SIGTERM для завершения контейнера | STOPSIGNAL SIGINT | Примечания:\\ **CMD**: * Может включать исполняемый файл; * Если не содержит никакого файла, обязательно должна быть ENTRYPOINT, в таком случае обе должны быть в формате JSON; * Аргументы при запуске docker-образа переопределяют аргументы в CMD; * По ходу сборки не фиксирует никакого результата; * Полезен когда нужно переопределять аргументы из строки запуска; **ENTRYPOINT**: * Dockerfile должен содержать либо CMD либо ENTRYPOINT; * Нужен когда нужно запустить одну и туже команду несколько раз; * "Используйте когда ваш контейнер выступает в роли исполняющейся программы" (?); **Установка пакетов**: * В Alpine образах есть какой то менеджер apk; * Можно использовать файл "requirements.txt" с перечислением необходимых пакетов; **WORKDIR**: * Создаст отсутствующие директории; * Можно использовать несколько инструкций; * Правило хорошего тона юзать абсолютные пути; **ARG**: * Значения не доступны в работающем контейнере, но можно использовать как дефолт на случай если не переопределили при билде, это фича, при запуске передаем нужное значение в "--build-arg"; * пример: ARG folder=/opt/apps WORKDIR $folder; docker build .. --build-arg folder=/my_fold
:!: Работа в консоли Экспорт/Импорт вольюма: # *container-name - имя контейнера к которому привязан вольюм; # *volume-path - путь монтирования вольюма внутри контейнера; # После команды, docker запустит контейнер и создаст архив с содержимым docker run --rm --volumes-from container-name -v $(pwd):/backup ubuntu bash -c "cd /volume-path && tar zcf /backup/backup.tar.gz ." # Для восстановления docker run --rm --volumes-from container-name -v $(pwd):/backup ubuntu bash -c "cd /volume-path && tar zxf /backup/backup.tar.gz" Еще команды. Работа с образами и контейнерами # -q возвращает только ID объектов docker rmi $(docker images -q) docker rm $(docker ps --filter status=exited -q) # Остановить все действующие контейнеры docker stop $(docker ps -a -q) docker stop $(docker ps -q --filter name=^web) # Принудительно удалить образы docker rmi -f <имя или ID>
==== Примеры ====
:!: Работа с контейнерами # Возобновить (уже созданный, остановленный) контейнер docker start [id] # Подключится к работающему контейнеру (после этого сложно отключится, вроде как ctrl+p ctrl+q но нифига не рабоатет, т.ч. лучше запускать шелл) docker attach [id] # Подключится и запустить доп процесс docker exec -it [id] bash # Работающие контейнеры docker ps [-a] docker image [list prune (-a) inspect] docker container [list prune (-a) inspect] # Процессы docker top # Ресурсы docker stats # Порты docker port # Проброс папок при запуске docker run -it -v /AbsoluteExistPath:/AbsDestFold NameImage # Создание образа. # Параметры создания указываются в файле "Dockerfile", он может находится где угодно, при сборке указывается путь к нему, внутри него указывается исходный образ docker build -t MyNewImage /PathToDockerfile # С удалением промежуточных образов docker build --rm -t local/myImage ./[Dockerfile] # docker-compose (apt install docker-compose) # Создаем нужный файл yml, в директории и из этой же директории запускаем проект docker-compose up [-d для фонового запуска] # Тегирование и пуш в registry docker tag local_image:latest REGISTRY_URL:RELEASE-BUILD_VERSION docker push REGISTRY_URL:RELEASE-BUILD_VERSION
:!: Пример Dockerfile FROM node:18-alpine WORKDIR /app COPY . . RUN yarn install --production CMD ["node", "src/index.js"] EXPOSE 3000 FROM eclipse-temurin:17-jdk-alpine as builder WORKDIR /opt/app COPY .mvn/ .mvn COPY mvnw pom.xml ./ RUN ./mvnw dependency:go-offline COPY ./src ./src RUN ./mvnw clean install FROM eclipse-temurin:17-jre-alpine WORKDIR /opt/app COPY --from=builder /opt/app/target/*.jar /opt/app/*.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "/opt/app/*.jar"]
==== Собственные образы ==== В PATH передается расположение файлов для сборки, не стоит передавать большое т.к. все содержимое передается демону докера т.к. он занимается сборкой, можно делать файл "игнора".\\ Сам образ создается в репозитории, в опциях можно указывать параметры для репозитория.\\ * **-f** - путь к файлу Dockerfile * **-t** - опции, можно указать имя создаваемого образа
:!: Пример: цикличный вывод из контейнеров в один общий файл PS При указании пути, тильда не работает\\ read.sh: #!/bin/bash while true do echo "--==" $(uname -a) "==--" >> /outdir/output.txt sleep 2 done Dockerfile: FROM debian MAINTAINER im RUN apt-get update && apt-get install procps -y COPY read.sh /scrptdir/read.sh CMD ["/scrpt/read.sh"] # Сборка docker build -t imecho . # Запуск docker run -dit --name contecho -v /home/dn/imread/outdir:/outdir imecho
:!: Докер файл с java приложением Сборка java уже была FROM openjdk:17 WORKDIR /app COPY ./build/libs/*.jar ./my_app.jar EXPOSE 8080/tcp CMD ["java", "-Dspring.profiles.active=stage", "-Duser.timezone=GMT", "-jar", "my_app.jar"] Сборку делаем здесь же FROM gradle:7-jdk17 as build WORKDIR /src COPY . . RUN --mount=type=cache,target=/home/gradle/.gradle . && \ gradle wrapper && \ ./gradlew compileJava && \ ./gradlew test && \ ./gradlew bootJar FROM openjdk:17 WORKDIR /app COPY ./build/libs/*.jar ./my_app.jar EXPOSE 8080/tcp CMD ["java", "-Dspring.profiles.active=stage", "-Duser.timezone=GMT", "-jar", "my_app.jar"]
==== Использование ==== Изменения сохраняются в контейнере, устанавливаемый софт например.\\ Создание с параметром **-it** (интерактивный с эмулятором) создает контейнер сразу с запущенным КИ, в противном случае без него и с-но останавливается сразу после запуска.\\ При повторном создании образа (через Dockerfile), образ пересоздается, типа следующая версия (latest), старый остается.\\ ===== Docker-compose ===== Оркестрация несколькими контейнерами\\ docker-compose up -d docker-compose down docker-compose -f filename -f filename2 up -d docker-compose -f filename -f filename2 down
:!: Info Сеть создается автоматически, общая для всех контейнеров, контейнеры доступны друг другу по имени хоста\\ Параметры можно менять/задавать, можно подключаться к уже созданной сети, разве что для этого и имеет смысл специально указывать сеть в компоуз-файле\\
:!: Пример приложения и БД postgres Приложение загружается с собственного registry, перед этим нужно выполнить "docker login"\\ Контейнеры объединяются в общую сеть\\ Создается постоянное хранилище, используемое базой данных\\ version: '3.7' networks: my_app_net: name: my_net volumes: pgdata: name: db_pgdata driver_opts: type: None device: /opt/pgdata o: bind services: my_appication: image: registry.myapp.io/myapp/ container_name: my_app restart: unless-stopped expose: - "8080" environment: - "DATABASE_URL=jdbc:postgresql://postgres:5432/" - "DATABASE_USER=" - "DATABASE_PASSWORD=" networks: - my_app_net depends_on: - postgres postgres: image: postgres:14 command: ["postgres", "-c", "config_file=/etc/postgresql/postgresql.conf"] container_name: postgres restart: unless-stopped ports: - "5432:5432" environment: - "POSTGRES_PASSWORD=" networks: - my_app_net volumes: - pgdata:/var/lib/postgresql/data - /opt//postgres/postgresql.conf:/etc/postgresql/postgresql.conf - /opt//postgres/pg_hba.conf:/etc/postgresql/pg_hba.conf - /opt//postgres/initdb_stage:/docker-entrypoint-initdb.d
:!: Kafka и Zookeeper version: '3.7' services: zoo1: image: zookeeper hostname: zoo1 container_name: zoo1 ports: - "2181:2181" environment: ZOOKEEPER_CLIENT_PORT: 2181 ZOOKEEPER_SERVER_ID: 1 ZOOKEEPER_SERVERS: zoo1:2888:3888 kafka1: image: confluentinc/cp-kafka:latest hostname: kafka1 container_name: kafka1 ports: - "9092:9092" environment: KAFKA_BROKER_ID: 1 KAFKA_ZOOKEEPER_CONNECT: "zoo1:2181" KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka1:19092,EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9092 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL # KAFKA_LOG4J_LOGGERS: "kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO" # KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 # KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 # KAFKA_JMX_PORT: 9999 # KAFKA_JMX_HOSTNAME: ${DOCKER_HOST_IP:-127.0.0.1} # KAFKA_AUTHORIZER_CLASS_NAME: kafka.security.authrizer.AclAuthorizer # KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: "true" depends_on: - zoo1 * KAFKA_ADVERTISED_LISTENERS - адреса для клиентов, в т.ч. для межброкерного взаимодействия * режим для межброкерного взаимодействия задается в "KAFKA_INTER_BROKER_LISTENER_NAME", но для этого в вышестоящем пар-ре должен быть прописан этот режим (internal/external)
:!: Еще пример с кафкой version: "2" services: zookeeper: image: docker.io/bitnami/zookeeper:3.9 ports: - "2181:2181" volumes: - "zookeeper_data:/bitnami" environment: - ALLOW_ANONYMOUS_LOGIN=yes kafka0: image: docker.io/bitnami/kafka:3.4 ports: - "9094:9094" volumes: - "kafka_data0:/bitnami" environment: - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 - KAFKA_CFG_BROKER_ID=0 - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094 - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka0:9092,EXTERNAL://localhost:9094 - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT depends_on: - zookeeper kafka1: image: docker.io/bitnami/kafka:3.4 ports: - "9096:9096" volumes: - "kafka_data1:/bitnami" environment: - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 - KAFKA_CFG_BROKER_ID=1 - KAFKA_CFG_LISTENERS=PLAINTEXT://:9095,CONTROLLER://:9097,EXTERNAL://:9096 - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka1:9095,EXTERNAL://localhost:9096 - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT depends_on: - zookeeper volumes: zookeeper_data: driver: local kafka_data0: driver: local kafka_data1: driver: local version: "2" services: zookeeper: image: docker.io/bitnami/zookeeper:3.9 ports: - "2181:2181" volumes: - "zookeeper_data:/bitnami" environment: - ALLOW_ANONYMOUS_LOGIN=yes kafka: image: docker.io/bitnami/kafka:3.4 ports: - "9092:9092" volumes: - "kafka_data:/bitnami" environment: - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 depends_on: - zookeeper volumes: zookeeper_data: driver: local kafka_data: driver: local
:!: