Table of Contents

Технология Docker

Методические идеи

Установка

Ubuntu/Debian

# apt install docker.io

Настройка registry-mirrors

# 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

Работа с образами и контейнерами

Обзор и удаление

# 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

Создание образа для приложения вручную

server# docker run -it --name webd --hostname webd debian bash

webd# apt update && apt install file procps nano
webd/# cat start.sh
#!/bin/sh

/etc/init.d/inetutils-inetd start

bash
server# docker ps -a

server# docker diff webd
server# docker start webd

server# docker attach webd

root@webd:/# chmod +x start.sh
server# docker commit webd test/webd

gitlab-runner@server:~$ docker images

Создание образа для приложения с использованием Dockerfile

Приложение 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 .
или
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

:~/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
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
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, по журналам определять какой контейнер используется

Процессы контейнера и системы

server# docker top webd01

server# ps axw | grep inetd
server# ps axw | grep start.sh

server# cat /proc/<PID>/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

Использование готовых образов приложений

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
# docker logs sftp01

# docker stop sftp01

# docker rm sftp01

docker-compose

# 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

# 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=="
                }
        }
}

Использование 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

# 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

~/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 <==
<HTML>
  <HEAD>
    <META HTTP-EQUIV="Refresh" CONTENT="10;URL=/cgi-bin/apwebd/">
  </HEAD>
  <BODY text="blue">
    <H1><A HREF=/cgi-bin/apwebd/>Login to ${APWEBD_HOSTNAME}</A></H1>
    Version: 1.2
  </BODY>
</HTML>
==> 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 <==
<IfModule mod_alias.c>
        <IfModule mod_cgi.c>
                Define ENABLE_USR_LIB_CGI_BIN
        </IfModule>

        <IfModule mod_cgid.c>
                Define ENABLE_USR_LIB_CGI_BIN
        </IfModule>

        <IfDefine 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/
                <Directory "/usr/lib/cgi-bin">
                        AllowOverride None
                        Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
#                       Require all granted
                        AuthType openid-connect
                        Require valid-user
                </Directory>
        </IfDefine>
</IfModule>
==> rootfs/usr/lib/cgi-bin/apwebd <==
#!/bin/sh

echo Content-type: text/html
echo

echo "<h1 style=\"color:blue;\">Hello ${OIDC_CLAIM_preferred_username}</h1>"

echo "<pre>"; env; echo "</pre>"
~/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

Старая версия

Работа с образами

# docker search debian

# docker pull debian

# docker images

# docker commit debian_cont_01 debian_img_01

# docker rmi debian_img_01

Работа с контейнерами

# 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

Вариант 2

Использование nat/dnat

# 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