Ищем коммит, в котором добавилась строчка. Флаг -S (pickaxe) ищет добавление/удаление конкретной строки. Флаги --reverse и --oneline позволят увидеть самый первый коммит (где строка появилась) на самом верху:
Git. Задача 2. Удаление секретных данных (passwords.txt)
Если пароли были залиты давно и нужно “выпилить” файл из всей истории удаленного репозитория, используется утилита git filter-branch (или git filter-repo, если установлена). Стандартный подход средствами Git “из коробки”:
Шаги решения:
10. Полностью удаляем файл из истории всех веток и индексов:
Принудительно отправляем переписанную историю на удаленный сервер (это затрет старые коммиты с паролем на сервере):
git push origin --force --all
(Если секретный файл залили только что в последнем коммите, достаточно сделать git rm --cached passwords.txt, затем git commit --amend и git push -f).
Git. Задача 3a. ТОП-3 контрибьютор и его второй коммит
Алгоритм решения:
13. Клонируем и переходим:
git clone https://github.com/RUB-NDS/PRET.gitcd PRET
Выводим список разработчиков, отсортированный по количеству коммитов:
git shortlog -sn
Смотрим на третью строчку сверху. Допустим, там разработчик с именем “Alice Smith”.
15. Ищем историю коммитов этого разработчика. Флаг --reverse покажет историю от самых старых к новым. Выводим только 2 первых коммита:
Хотя ссылка недоступна, задача на оптимизацию образа классическая.
Основные принципы и шаги, которые нужно продемонстрировать в измененном Dockerfile:
22. Легковесный базовый образ: Использовать alpine или slim версии. (Заменить FROM python:3.9 на FROM python:3.9-alpine).
23. Объединение слоев (RUN): Вместо нескольких директив RUN объединить их через &&.
24. Очистка кэша: После установки пакетов удалять кэш пакетного менеджера в том же слое.
25. Многоэтапная сборка (Multi-stage build): Если код нужно компилировать (Go, C++), сборка идет в тяжелом образе builder, а готовый бинарник копируется в пустой scratch или alpine образ.
26. Файл .dockerignore: Исключить из копирования node_modules, .git, виртуальные окружения.
Алгоритм решения:
27. Создаем директорию на хосте для монтирования:
mkdir exam_docker
Пишем Dockerfile:
FROM python:latest# Установка нужных библиотекRUN pip install --no-cache-dir matplotlib scipy# Установка рабочей директорииWORKDIR /app# Создание файла внутри образаRUN echo "Hi!" > readme.txt# Чтобы контейнер не завершился сразу, оставляем его работать (или запускаем bash)CMD ["sleep", "infinity"]
Собираем образ:
docker build -t python_exam .
Важный нюанс монтирования (bind mount): Если примонтировать пустую хостовую папку ./exam_docker прямо в /app, она “перекроет” содержимое директории /app в контейнере, и созданный файл readme.txt станет невидим.
Поэтому мы копируем созданный файл в подмонтированную директорию при старте:
docker run -d --name py_exam -v $(pwd)/exam_docker:/mnt_folder python_exam sh -c "cp /app/readme.txt /mnt_folder/ && sleep infinity"
Docker. Задача 3. Линтеры и чекеры Python
Алгоритм решения:
31. Пишем Dockerfile:
FROM python:3.10-slimRUN pip install --no-cache-dir ruff banditWORKDIR /project# Создаем директорию для отчетовRUN mkdir -p /linters# Запускаем чекеры. Используем символ ; или || true, чтобы падение одного чекера # не останавливало выполнение следующих команд. # Сохраняем в JSON и TXT форматах, а также выводим в консоль (stdout).CMD ruff check /project --output-format json > /linters/ruff.json ; \ ruff check /project > /linters/ruff.txt ; \ bandit -r /project -f json -o /linters/bandit.json || true; \ bandit -r /project > /linters/bandit.txt || true; \ cat /linters/ruff.txt && cat /linters/bandit.txt
Собираем образ:
docker build -t my_linters .
Запускаем контейнер (прокидываем код проекта внутрь, а результаты вытаскиваем наружу):
# Предполагается, что код лежит в ./mycode, а отчеты хотим получить в ./reportsdocker run --rm \ -v $(pwd)/mycode:/project \ -v $(pwd)/reports:/linters \ my_linters
После отработки контейнер удалится (--rm), в консоли отобразятся отчеты, а в папке ./reports появятся файлы .json и .txt.
Docker. Задача 4. Царь горы (Гонка за файл)
Суть: Нужно запустить контейнеры, шарящие один и тот же volume.
Шаги решения:
34. Создаем именованный том Docker:
docker volume create arena_vol
Запускаем контейнер-создатель (работает в бесконечном цикле):
docker run -d --name creator -v arena_vol:/data alpine sh -c "while true; do touch /data/delete_it.txt; done"
Запускаем несколько контейнеров-удаляторов. Скрипт будет пытаться удалить файл, и если удаление успешно (файл существовал), увеличивать счетчик:
Контейнер 1:
docker run -d --name deleter1 -v arena_vol:/data alpine sh -c "score=0; while true; do if rm /data/delete_it.txt 2>/dev/null; then score=\$((score+1)); echo \"Deleter1 score: \$score\"; fi; done"
Контейнер 2:
docker run -d --name deleter2 -v arena_vol:/data alpine sh -c "score=0; while true; do if rm /data/delete_it.txt 2>/dev/null; then score=\$((score+1)); echo \"Deleter2 score: \$score\"; fi; done"
Чтобы посмотреть, кто побеждает, смотрим логи:
docker logs -f deleter1docker logs -f deleter2
Compose. Задача 1. RMQ
Ссылка недоступна, но задача обычно требует поднятия брокера сообщений RabbitMQ с management-плагином для доступа к веб-интерфейсу.
Файл docker-compose.yaml:
services: rabbitmq: image: rabbitmq:3-management container_name: rmq_server ports: - "5672:5672" # Порт для подключения приложений (AMQP) - "15672:15672" # Порт веб-интерфейса управления environment: RABBITMQ_DEFAULT_USER: admin RABBITMQ_DEFAULT_PASS: password volumes: - rmq_data:/var/lib/rabbitmqvolumes: rmq_data:
Запуск:docker compose up -d
Compose. Задача 2. Telegram-сервис и генератор
Файл docker-compose.yaml:
services: sender: image: python:3.10-slim # Или ваш собранный образ sender_image container_name: tg_sender # Команда-заглушка для примера, в реальности тут запуск API сервера command: python -m http.server 8080 environment: - TELEGRAM_TOKEN=123456789:AAH... ports: - "8080:8080" generator: image: alpine/curl # Или ваш собранный образ generator_image container_name: tg_generator # Отправка GET запроса каждые 2 секунды command: sh -c "while true; do curl -s http://sender:8080/send?msg=hello; sleep 2; done" depends_on: - sender
Описание взаимодействия:
Docker Compose автоматически создает bridge-сеть default и подключает к ней оба контейнера. Встроенный DNS-сервер позволяет контейнеру generator обращаться к контейнеру sender напрямую по его имени в файле (хост sender, порт 8080). Проброс портов (ports: "8080:8080") нужен только для доступа снаружи (с хост-машины), для внутреннего общения генератора с сендером он не обязателен. Директива depends_on гарантирует, что generator запустится только после успешного старта контейнера sender.
Compose. Задача 3. Обожаю запах реплик по утрам
Файл docker-compose.yaml:
services: web: image: python:3.10-alpine # Крошечный скрипт, читающий переменную окружения и отдающий ответ (для примера) # В реальности здесь будет build: . command: sh -c 'echo -e "HTTP/1.1 200 OK\n\nHi, $USERNAME?" > response.txt && nc -lk -p 8000 -e cat response.txt' environment: - USERNAME=Examiner deploy: mode: replicated replicas: 5 # Поднимает 5 реплик сервиса ports: - "1501-1505:8000" # Диапазон портов для привязки реплик
Пояснение:
Директива deploy.replicas: 5 говорит Compose создать 5 идентичных контейнеров.
Так как нельзя привязать 5 контейнеров к одному физическому порту хоста (возникнет конфликт), мы используем синтаксис диапазонов 1501-1505:8000. Docker автоматически распределит 5 контейнеров (их внутренний порт 8000) по внешним портам хоста от 1501 до 1505 по порядку. Запуск: docker compose up -d.
docker build -t my_ubuntu_app .docker run -d my_ubuntu_app
7. Сборка (с COPY), запуск и проброс портов
Dockerfile:(такой же, как в задаче 5)Команды в терминале:
docker build -t my_ubuntu_app .docker run -p 3080:80 my_ubuntu_app
8. Сборка (с COPY), запуск и монтирование (Volume)
Dockerfile:(такой же, как в задаче 5)Команды в терминале:(Нюанс: при монтировании папки с хоста поверх /contfoldername, она полностью перекроет то, что было скопировано директивой COPY во время сборки. Тем не менее, команда выглядит так):
docker build -t my_ubuntu_app .docker run -v $(pwd)/foldername:/contfoldername my_ubuntu_app
Блок задач: Docker (Python + pip)
9. Сборка и запуск (Python, pip)
Dockerfile:
FROM python:3.7RUN pip install pkg1 pkg2CMD ["python", "scriptname.py"]
Команды в терминале:
docker build -t my_python_app .docker run my_python_app
10. Сборка и запуск в фоновом режиме (Python)
Dockerfile:(такой же, как в задаче 9)Команды в терминале:
docker build -t my_python_app .docker run -d my_python_app
11. Сборка, запуск и проброс портов (Python)
Dockerfile:(такой же, как в задаче 9)Команды в терминале:
docker build -t my_python_app .docker run -p 3080:80 my_python_app
12. Сборка, запуск и монтирование (Python)
Dockerfile:
FROM python:3.7RUN pip install pkg1 pkg2WORKDIR /contfoldernameCMD ["python", "scriptname.py"]
Команды в терминале:
docker build -t my_python_app .docker run -v $(pwd)/foldername:/contfoldername my_python_app
Отправляем изменения ветки b1 на удаленный сервер:
git push origin b1
Git 4. Скачивание изменений и слияние (Pull)
Алгоритм решения:
Команда git pull автоматически скачивает новые коммиты с удаленного сервера (fetch) и объединяет их с твоей локальной веткой (merge).
# Простой вариант (если удаленная ветка уже связана с локальной):git pull# Явный вариант (скачать и слить изменения из удаленной ветки main):git pull origin main
Альтернативный способ (разбитый на два шага для безопасности):