====== Технология Docker ====== * [[https://youtu.be/hdVNKmru3LM|youtube/Проникновение в Docker с примерами, Дмитрий Столяров, Flant]] * [[https://www.upguard.com/articles/docker-vs-lxc|Docker vs LXC]] * [[https://youtu.be/QF4ZF857m44|youtube/Артем Матяшов/Основы Docker. Большой практический выпуск]] ===== Методические идеи ===== * Добавить запись в журнал передаваемых скрипту webd заголовков * Использовать переменную окружения, передаваемую контейнеру для включения/выключения режима отладки/записи в журнал ===== Установка ===== ==== Ubuntu/Debian ==== * [[https://docs.docker.com/engine/install/ubuntu/|Install Docker Engine on Ubuntu]] * [[https://docs.docker.com/engine/installation/linux/docker-ce/debian/|Get Docker CE for Debian]] # apt install docker.io ==== Настройка registry-mirrors ==== * [[https://habr.com/ru/news/818177/|Docker hub перестал работать в России]] # cat /etc/docker/daemon.json { "registry-mirrors": ["https://mirror.gcr.io"] } ==== Настройка загрузки образов через proxy ==== * [[Переменные окружения]] # systemctl edit docker.service ... [Service] #Environment="http_proxy=http://proxy.isp.un:3128/" Environment="https_proxy=http://proxy.isp.un:3128/" Environment="no_proxy=localhost,127.0.0.1,isp.un,corpX.un" ... # systemctl daemon-reload # service docker restart ==== Тестирование установки ==== # systemctl status docker # docker info # docker run hello-world # docker events --since '10m' ==== Предоставление прав непривилегированным пользователям ==== # usermod -aG docker gitlab-runner # su - gitlab-runner ===== Работа с образами и контейнерами ===== * [[https://www.baeldung.com/ops/docker-image-layers-sizes|Finding the Layers and Layer Sizes for a Docker Image]] * [[https://stackoverflow.com/questions/37966973/what-is-the-difference-between-the-size-and-the-virtual-size-of-the-docker-image|What is the difference between the size and the virtual size of the docker images?]] * [[https://docs.docker.com/engine/reference/commandline/system_prune/|docker system prune - Remove unused data]] * [[Сервис Grafana]] ==== Обзор и удаление ==== # docker images # docker ps -a #--size # docker container ls -a # docker container stats # docker start -i NNNNNNNNNNN # docker rm $(docker ps -aq) # docker rm $(docker ps -q -f status=exited) # docker rmi hello-world # docker rmi -f $(docker images -aq) # docker system df # docker system prune # docker system prune -a --volumes ==== Копирование файлов в контейнер ==== root@webinar:~# docker cp ca.crt greenlight-v3:/usr/local/share/ca-certificates/ root@webinar:~# docker exec -ti greenlight-v3 /usr/sbin/update-ca-certificates root@webinar:~# docker exec greenlight-v3 wget -O /dev/null https://keycloak.corp13.un root@webinar:~# docker commit greenlight-v3 bigbluebutton/greenlight:v3 ==== Создание образа для приложения вручную ==== * !!! каталог и содержимое /var/www создать в хост системе а скрипт webd в контейнере * [[Средства программирования shell#Ресурсы Web сервера на shell]] server# docker run -it --name webd --hostname webd debian bash webd# apt update && apt install file procps nano * [[Сервис INETD]] * [[Средства программирования shell#Web сервер на shell]] webd/# cat start.sh #!/bin/sh /etc/init.d/inetutils-inetd start bash * Ctrl+D server# docker ps -a server# docker diff webd * Методически, "вспомнили", что "забыли" сделать скрипт start.sh "выполнимым" server# docker start webd server# docker attach webd root@webd:/# chmod +x start.sh * Методически, можно уже на этом этапе: запустить скрипт ./start.sh, отключится от контейнера без его остановки Ctrl+P, Q(still holding Ctrl), выяснить через docker inspect webd назначенный контейнеру ip, проверить через curl работу приложения и, если все работает (правда, будет сообщение, что такого файла нет), сделать docker stop webd, и commit * [[https://cpab.ru/kak-rabotajut-tegi-docker-cloudsavvy-it/|Как работают теги Docker?]] * [[https://code.tutsplus.com/ru/tutorials/docker-from-the-ground-up-understanding-images--cms-28165|Докер с нуля: понимание слоев образов]] * Пакет RSYNC - [[Пакет RSYNC#Инкрементное копирование (Incremental Backup)]] server# docker commit webd test/webd gitlab-runner@server:~$ docker images * [[#Запуск в режиме демона и подключение к контейнеру]] из полученного образа ==== Создание образа для приложения с использованием Dockerfile ==== * [[Сервис TACACS+]] === Приложение bash webd === server# mkdir -p /root/webd/ && cd /root/webd/ или gitlab-runner@server:~$ mkdir -p ~/webd/webd/ && cd ~/webd/webd/ server# cp /usr/local/sbin/webd . lan:~/webd# scp server:/usr/local/sbin/webd . или * [[Средства программирования shell#Web сервер на shell]] gitlab-runner@server:~/webd/webd$ nano webd # добавляем закомментированные строки server# ###tar -cvzf www.tgz -C /var/ www/ server# cat start.sh #!/bin/sh /etc/init.d/inetutils-inetd start touch /var/log/webd.log #chown 10003 /var/www/ if [ "$MYMODE" = 'TEST' ]; then sleep 3 curl localhost && exit 0 || exit 1 else tail -f /var/log/webd.log fi server# cat Dockerfile #FROM debian FROM debian:bullseye #FROM debian:bookworm RUN cp /usr/share/zoneinfo/Etc/GMT-3 /etc/localtime \ && apt-get update \ && apt-get install -y inetutils-inetd file curl\ && apt-get clean \ && echo 'www stream tcp nowait root /usr/local/sbin/webd webd' > /etc/inetd.conf COPY start.sh / COPY webd /usr/local/sbin/webd ### ADD www.tgz /var/ ### for helm readiness/liveness Probe ### COPY index.html /var/www/ EXPOSE 80 #ENV MYMODE=TEST ENTRYPOINT ["/start.sh"] # docker build -t test/webd . # docker run --rm -e MYMODE=TEST test/webd # docker history test/webd * [[#Запуск в режиме демона и подключение к контейнеру]] === Приложение python pywebd === * [[Язык программирования Python]] * [[https://stackoverflow.com/questions/49955097/how-do-i-add-a-user-when-im-using-alpine-as-a-base-image|How do I add a user when I'm using Alpine as a base image?]] :~/pywebd$ dpkg -l | grep python :~/pywebd$ cat Dockerfile FROM python:3.11-alpine #RUN pip install --root-user-action=ignore --upgrade pip #RUN adduser -D myuser #USER myuser #WORKDIR /home/myuser COPY requirements.txt . #COPY --chown=myuser:myuser requirements.txt . #ENV PATH="/home/myuser/.local/bin:${PATH}" RUN pip install -r requirements.txt #RUN pip install --user -r requirements.txt COPY . . #COPY --chown=myuser:myuser . . ENTRYPOINT ["python"] CMD ["app.py"] :~/pywebd$ time docker build -t pywebd . :~/pywebd$ docker run -d --rm -p 8080 --name pywebd01 pywebd * [[#Запуск в режиме демона и подключение к контейнеру]] === Приложение golang gowebd === == Dockerfile Multistage Building == * [[Язык программирования Golang]] * [[https://habr.com/ru/articles/647255/|Рекомендации по работе с Docker для Golang-разработчиков (Multistage Building)]] * [[https://www.docker.com/blog/containerize-your-go-developer-environment-part-2/|Containerize Your Go Developer Environment – Part 2]] student@client1:~/gowebd$ cat Dockerfile FROM golang #FROM golang as builder WORKDIR /build COPY . . RUN test -e go.mod || go mod init gowebd #ENV CGO_ENABLED=0 RUN go build -o /gowebd #FROM alpine #COPY --from=builder /gowebd /gowebd ENTRYPOINT ["/gowebd"] student@client1:~/gowebd$ docker images student@client1:~/gowebd$ time docker build -t gowebd . real 6m2.564s student@client1:~/gowebd$ docker run -d -p 8080:80 --rm gowebd student@client1:~/gowebd$ docker run -d --rm -p 80 --name gowebd01 gowebd * [[#Запуск в режиме демона и подключение к контейнеру]] == docker buildx == * [[https://doroshev.com/blog/docker-mount-type-cache/|Docker Buildkit: Правильное использование --mount=type=cache]] ubuntu:~/gowebd# apt install docker-buildx ubuntu:~/gowebd# cat Dockerfile ... RUN --mount=type=cache,target="/root/.cache/go-build" go build -o /gowebd ... ubuntu:~/gowebd# time docker build -t gowebd . ==== Запуск в режиме демона и подключение к контейнеру ==== 1-й пример - запуск образа сделанного "вручную", можно запустить несколько экземпляров с -p 80 выяснить назначенные порты, настроить keepalived и провести нагрузочное тестирование server# docker run --name webd01 --hostname webd01 -itd -v /var/www/:/var/www/ -p 8000:80 test/webd /start.sh 2-й пример - через Dockerfile задан entrypoint и expose, ключ --rm для удаления контейнера после остановки, добавить, при необходимости, -v server# docker run --name webd01 -e MYMODE=TEST -itd --rm -P test/webd 3-й раз - запустить несколько экземпляров, указав параметры для подключения внешнего каталога /var/www/, выяснить назначенные порты, настроить keepalived, по журналам определять какой контейнер используется === Процессы контейнера и системы === * [[Технология cgroup]] * [[https://www.baeldung.com/ops/docker-memory-limit|Setting Memory And CPU Limits In Docker]] * [[https://stackoverflow.com/questions/72185669/what-is-the-real-memory-available-in-docker-container|What is the real memory available in Docker container?]] * [[Технология namespaces]] server# docker top webd01 server# ps axw | grep inetd server# ps axw | grep start.sh server# cat /proc//cgroup server# systemd-cgls cgroup-v1# cat /sys/fs/cgroup/memory/docker/NNNNNNNNNNNNNNNNNNNNNNNNNNNNN/memory.max_usage_in_bytes cgroup-v2# cat /sys/fs/cgroup/system.slice/docker-NNNNNNNNNNNNNNNNNNNNNNNNNNNNN.scope/memory.max server# docker stats server# lsns | grep start.sh === Анализ параметров запущенного контейнера === server# docker inspect webd01 server# docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' webd01 server# wget -qO - http://172.17.0.2/ server$ curl --noproxy '*' http://172.17.0.2/ server# docker port webd01 server# docker logs webd01 node1# docker logs webd01 -f server# wget -qO - http://localhost:8000/ server$ curl http://localhost:8000 server$ curl http://localhost:8000/not_exit_file host browser -> http://server.corpX.un:8000/ server# docker attach webd01 server# docker exec -it webd01 bash webd01# ps ax или webd01# ls /proc/ webd01# cat /proc/1/cmdline webd01# ss -tpan или webd01# cat /proc/net/tcp webd01# cat /proc/net/tcp6 Ctrl+P, Q(still holding Ctrl) server# docker stop webd01 server# docker inspect webd01 server# docker start webd01 host browser -> http://server.corpX.un:8000/ server# docker stop webd01 && docker rm webd01 ==== Использование готовых образов приложений ==== * [[https://hub.docker.com/search?type=image|Explore Docker's Container Image Repository]] === atmoz/sftp === * [[https://hub.docker.com/r/atmoz/sftp|atmoz/sftp]] # docker search sftp # chown -R 10003 /var/www # docker run --name sftp01 -v /var/www:/home/user3/www -p 2222:22 -d atmoz/sftp user3:password3:10003 # docker exec -it sftp01 bash Ctrl+D # docker top sftp01 # sftp -P 2222 user3@localhost * [[Сервис SSH#Настройка ssh клиента]] # docker logs sftp01 # docker stop sftp01 # docker rm sftp01 ===== docker-compose ===== * [[https://habr.com/ru/company/ruvds/blog/450312/|Руководство по Docker Compose для начинающих]] * [[https://stackoverflow.com/questions/39663096/docker-compose-creating-multiple-instances-for-the-same-image|docker-compose creating multiple instances for the same image]] * [[Инструмент GitLab#Установка через docker-compose]] GitLab * Установка через [[Сервис Keycloak#docker-compose]] Keycloak # apt install docker-compose # cat docker-compose.yml version: "3" services: webd: # image: server.corpX.un:5000/student/webd:ver1.N image: test/webd # build: webd/ entrypoint: /start.sh # ports: # - "80" volumes: - /var/www/:/var/www/ # - vol1:/var/www/ deploy: mode: replicated replicas: 3 # environment: # - MYMODE=TEST # stdin_open: true tty: true sftp: image: atmoz/sftp ports: - "2222:22" volumes: - /var/www/:/home/user3/www # - vol1:/home/user3/www command: user3:password3:10003 #volumes: # vol1: # ###docker-compose build # docker-compose up -d # docker-compose stop # docker-compose start # docker-compose down # docker-compose rm # docker volume rm root_vol1 # docker-compose up -d --scale webd=N # docker ps gitlab-runner@server:~/webd$ cat docker-compose.yml version: "3" services: webd: image: server.corpX.un:5000/student/webd:ver1.N ports: - "80" volumes: - /var/www/:/var/www/ deploy: mode: replicated replicas: 3 node1,2,3# docker-compose --compatibility up -d node1,2,3# docker-compose --compatibility down node1,2,3# docker ps -q | xargs -l docker port | sort -n ===== Локальные репозитории ===== ==== Копирование образов ==== server# docker save -o test-webd.tgz test/webd lan# scp server:test-webd.tgz . lan# docker load -i test-webd.tgz ==== Insecure Private Registry ==== * [[Инструмент GitLab#GitLab Docker Registry]] * [[https://docs.docker.com/registry/insecure/|Test an insecure registry/Deploy a plain HTTP registry]] # cat /etc/docker/daemon.json { "insecure-registries" : ["server.corpX.un:5000"] } # service docker restart ==== Аутентификация в Registry ==== gitlab-runner@server:~$ docker login gitlab-runner@server:~$ docker login http://server.corpX.un:5000 gitlab-runner@server:~$ less ~/.docker/config.json { "auths": { "server.corpX.un:5000": { "auth": "c3R1ZGVudDpQYSQkdzByZA==" } } } * [[https://serverfault.com/questions/703344/how-to-remove-an-image-tag-in-docker-without-removing-the-image-itself|How to remove an image tag in Docker without removing the image itself?]] ==== Использование Private Registry ==== gitlab-runner@server:~$ docker images gitlab-runner@server:~$ docker tag test/webd server.corpX.un:5000/student/webd gitlab-runner@server:~$ docker tag test/webd server.corpX.un:5000/student/webd:1.1 gitlab-runner@server:~$ docker images gitlab-runner@server:~$ docker push server.corpX.un:5000/student/webd gitlab-runner@server:~$ docker push server.corpX.un:5000/student/webd:1.1 ... node1_2_3# docker run --name webd01 --hostname webd01 -itd --rm -p 8000:80 server.corpX.un:5000/student/webd node1_2_3# docker run --name webd0N --hostname webd0N -itd --rm -P -v /var/www/:/var/www/ server.corpX.un:5000/student/webd ==== Использование образа Docker Registry on-premise ==== * [[https://docs.docker.com/registry/|Docker Registry]] * [[https://stackoverflow.com/questions/25436742/how-to-delete-images-from-a-private-docker-registry?newreg=e655d7146b114f0f9b88b1132990f346|How to delete images from a private docker registry?]] # docker run -d -p 5000:5000 -v /root:/certs \ -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/wild.crt \ -e REGISTRY_HTTP_TLS_KEY=/certs/wild.key \ --name registry --restart=always registry:2 # docker tag gowebd server.corp13.un:5000/student/gowebd # docker push server.corp13.un:5000/student/gowebd # curl https://server.corp13.un:5000/v2/_catalog {"repositories":["student/gowebd"]} # curl https://server.corp13.un:5000/v2/student/gowebd/tags/list ===== Дополнительная информация ===== ==== Инструмент kaniko ==== * [[https://habr.com/ru/companies/slurm/articles/436126/|Kubernetes: сборка образов Docker в кластере, можно использовать kaniko]] ~/gowebd# time docker run \ -v $(pwd):/workspace --rm\ gcr.io/kaniko-project/executor:latest \ --skip-tls-verify --log-timestamp\ --dockerfile=./Dockerfile \ --context=/workspace \ --destination=server.corp13.un:5000/student/gowebd \ --cache=true --cache-copy-layers --cache-repo server.corp13.un:5000/dev-cache --use-new-run \ --verbosity debug или, интерактивно: root@ubuntu:~/gowebd# docker run -it \ -v $(pwd):/workspace --rm --entrypoint "/bin/sh" \ gcr.io/kaniko-project/executor:debug # time /kaniko/executor --skip-tls-verify --log-timestamp \ --dockerfile=./Dockerfile \ --context=/workspace \ --destination=server.corp13.un:5000/student/gowebd тестируем результат: # docker run --pull=always --name gowebd -itd --rm \ -p 8000:80 server.corp13.un:5000/student/gowebd ==== Приложение apwebd ==== ~/apwebd$ cat Dockerfile FROM debian:bookworm RUN cp /usr/share/zoneinfo/Etc/GMT-3 /etc/localtime \ && apt-get update \ && apt-get install -y findutils gettext-base apache2 libapache2-mod-auth-openidc \ && apt-get clean \ && a2enmod cgid \ && a2enmod auth_openidc COPY rootfs/ / EXPOSE 80 ENTRYPOINT ["/start.sh"] ~/apwebd$ find rootfs/ -type f | xargs tail -n +1 ==> rootfs/var/www/html/index.html.apwebd-template <==

Login to ${APWEBD_HOSTNAME}

Version: 1.2
==> rootfs/start.sh <== #!/bin/sh [ "$APWEBD_HOSTNAME" ] || { echo Please set env APWEBD_HOSTNAME; exit; } [ "$KEYCLOAK_HOSTNAME" ] || { echo Please set env KEYCLOAK_HOSTNAME; exit; } [ "$REALM_NAME" ] || { echo Please set env REALM_HOSTNAME; exit; } find / -type f -name '*.apwebd-template' | while read -r FILE; do envsubst < "$FILE" > "${FILE%.apwebd-template}"; done /etc/init.d/apache2 start tail -f /var/log/apache2/error.log -f /var/log/apache2/access.log ==> rootfs/etc/apache2/conf-available/serve-cgi-bin.conf.apwebd-template <== Define ENABLE_USR_LIB_CGI_BIN Define ENABLE_USR_LIB_CGI_BIN OIDCSSLValidateServer Off OIDCProviderMetadataURL https://${KEYCLOAK_HOSTNAME}/realms/${REALM_NAME}/.well-known/openid-configuration OIDCRedirectURI http://${APWEBD_HOSTNAME}/cgi-bin/apwebd OIDCClientID any-client OIDCCryptoPassphrase anystring ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ AllowOverride None Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch # Require all granted AuthType openid-connect Require valid-user ==> rootfs/usr/lib/cgi-bin/apwebd <== #!/bin/sh echo Content-type: text/html echo echo "

Hello ${OIDC_CLAIM_preferred_username}

" echo "
"; env; echo "
"
~/apwebd# chmod +x rootfs/usr/lib/cgi-bin/apwebd rootfs/start.sh ~/apwebd$ docker build -t server.corp13.un:5000/student/apwebd:ver1.2 . ~/apwebd$ docker run -e APWEBD_HOSTNAME=apwebd.corp13.un -e KEYCLOAK_HOSTNAME=keycloak.corp13.un -e REALM_NAME=corp13 -itd --rm -P server.corp13.un:5000/student/apwebd:ver1.2 ~/apwebd$ docker run -e APWEBD_HOSTNAME=apwebd.corp13.un -e KEYCLOAK_HOSTNAME=keycloak.corp13.un -e REALM_NAME=corp13 -itd --entrypoint bash server.corp13.un:5000/student/apwebd:ver1.2 ~/apwebd$ docker push server.corp13.un:5000/student/apwebd:ver1.2 ===== Старая версия ===== ==== Работа с образами ==== * [[https://hub.docker.com/|Docker Hub]] # docker search debian # docker pull debian # docker images # docker commit debian_cont_01 debian_img_01 # docker rmi debian_img_01 ==== Работа с контейнерами ==== * ключи -i -t необходимы для возможности интерактивно (attach) подключаться к запущенному контейнеру # docker create -i -t --name debian_cont_01 debian # docker ps -a # docker container ls -a # docker update --restart=always debian_cont_01 # docker start debian_cont_01 # docker ps # docker container ls # docker inspect debian_cont_01 # docker top debian_cont_01 # docker attach debian_cont_01 :/# apt update :/# apt install iputils-ping :/# ping -c1 ya.ru Ctrl+P, Q(still holding Ctrl) # docker stop debian_cont_01 # docker rm debian_cont_01 # docker rm $(docker ps -aq) ==== Работа с сетью ==== === Управление сетями === # docker network ls # docker network create --subnet=192.168.200+X.0/24 corpX_dmz # docker run -h mail.corpX.un --net corpX_dmz --ip 192.168.200+X.10 -i -t --name debian_cont_01 debian # docker network inspect corpX_dmz === Назначение публичного ip для контейнера === == Вариант 1 == Использование bridge * [[https://jpetazzo.github.io/2013/10/16/configure-docker-bridge-network/|How to configure Docker to start containers on a specific IP address range]] == Вариант 2 == Использование nat/dnat * [[http://stackoverflow.com/questions/27937185/assign-static-ip-to-docker-container|Assign static IP to Docker container]] * присваиваем публичные ip адреса интерфейсу host машины и настраиваем NAT/DNAT связывающие публичные ip host машины со статическими ip контейнеров # ip addr add 172.16.1.100+X dev eth2 # iptables -t nat -A POSTROUTING -o eth2 -s 192.168.100+X.10 -j SNAT --to-source 172.16.1.100+X # iptables -t nat -A PREROUTING -i eth2 --destination 172.16.1.100+X -j DNAT --to-destination 192.168.100+X.10 ==== HEARTBEAT и Docker ==== nodeN# cat haresources node1.corpX.un drbddisk Filesystem::/dev/drbd0::/disk2::ext4 docker