====== Технология 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 <==
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