### 1. Многоступенчатая сборка образов #### 1.1.1 ````Dockerfile FROM mcr.microsoft.com/dotnet/sdk:7.0 AS api_build WORKDIR /src COPY ./TodoApi /src RUN dotnet restore TodoApi.csproj RUN dotnet build TodoApi.csproj -c Release -o /app/build FROM api_build AS publish RUN dotnet publish TodoApi.csproj -c Release -o /app/publish FROM node:21 AS ui_build WORKDIR /src COPY ./todo_ui /src RUN npm install RUN npm run build FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base WORKDIR /app COPY --from=publish /app/publish . COPY --from=ui_build /src/wwwroot ./wwwroot ENTRYPOINT ["./TodoApi"] ```` В директории todo_app: ````bash docker build -t todo-bundle . ```` #### 1.1.2 Создание сети: ````bash docker network create todo ```` Создание тома для PostgerSQL: ````bash docker volume create todo_pgdata ```` Запуск PostgreSQL: ````bash docker run -d --name=todo_postgres --network=todo -e POSTGRES_DB=todo -e POSTGRES_USER=todo -e POSTGRES_PASSWORD=todo --mount type=bind,source=./initdb,target=/docker-entrypoint-initdb.d --mount type=volume,source=todo_pgdata,target=/var/lib/postgresql/data postgres:16 ```` #### 1.1.3 ````bash docker run --rm -p 8080:80 --name todo_bundle -e 'ConnectionStrings__PostgreSQL=Host=todo_postgres;Port=5432;Database=todo;Username=todo;Password=todo' --name=todo_bundle --network=todo -t todo-bundle ```` #### 1.2.1 ````bash docker container rm -f todo_bundle && docker image rm -f todo-bundle ```` Докер-файл для API-сервиса ````Dockerfile FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build WORKDIR /src COPY . /src RUN dotnet restore TodoApi.csproj RUN dotnet build TodoApi.csproj -c Release -o /app/build FROM build AS publish RUN dotnet publish TodoApi.csproj -c Release -o /app/publish FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["./TodoApi"] ```` Сборка образа API-сервиса: (из директории `TodoApi`) ````bash docker build -t todo-api . ```` Конфигурация Nginx: ````nginx user nginx; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; #gzip on; keepalive_timeout 65; server { listen 80; location / { root /var/www/todo; index index.html; try_files $uri $uri/ /index.html =404; } location /api { proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_buffers 8 64k; proxy_busy_buffers_size 128k; proxy_buffer_size 64k; client_max_body_size 10m; proxy_http_version 1.1; proxy_pass http://todo_api; } } } ```` Докер-файл для JS-приложения: ````Dockerfile FROM node:21 AS ui_build WORKDIR /build COPY . /build RUN npm install RUN npm run build FROM nginx AS base WORKDIR /var/www/todo COPY --from=ui_build /build/nginx.conf /etc/nginx/nginx.conf COPY --from=ui_build /build/wwwroot ./ ```` Сборка образа UI: (из директории `todo_ui`) ````bash docker build -t todo-ui . ```` #### 1.2.2 Запуск API-сервиса: ````bash docker run -d --name=todo_api --network=todo -e 'ConnectionStrings__PostgreSQL=Host=todo_postgres;Port=5432;Database=todo;Username=todo;Password=todo' todo-api ```` Запуск UI: ````bash docker run -d -p 8080:80 --name=todo_ui --network=todo todo-ui ```` #### 1.2.3 ````Dockerfile FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim AS build WORKDIR /src COPY . /src RUN dotnet restore TodoApi.csproj RUN dotnet build TodoApi.csproj -c Release -o /app/build FROM build AS publish RUN dotnet publish TodoApi.csproj --self-contained -r linux-x64 -c Release -o /app/publish FROM debian:bullseye-slim AS base WORKDIR /app RUN apt-get update && apt-get install libicu67 COPY --from=publish /app/publish . ENTRYPOINT ["./TodoApi"] ```` В директории `TodoApi`: ````bash docker build -t todo-api . ```` Запуск, как и выше: ````bash docker run -d --name=todo_api --network=todo -e 'ConnectionStrings__PostgreSQL=Host=todo_postgres;Port=5432;Database=todo;Username=todo;Password=todo' todo-api ```` ### 2. Политики перезапуска #### 1. Добавить в `docker run` опцию `--restart=unless-stopped` #### 2. ````bash docker run -d --name=todo_postgres --network=todo -e POSTGRES_DB=todo -e POSTGRES_USER=todo -e POSTGRES_PASSWORD=todo --mount type=bind,source=./initdb,target=/docker-entrypoint-initdb.d --mount type=volume,source=todo_pgdata,target=/var/lib/postgresql/data --health-cmd='pg_isready -U todo -d todo' --health-interval=10s --health-retries=5 --health-timeout=5s --health-start-period=10s --restart=unless-stopped postgres:16 ```` ### 3. Ограничения ресурсов #### 1. Добавить опцию `--memory` в `docker run` #### 2. Добавить опцию `--cpus` в `docker run` ### 4. Docker compose #### 1. `docker compose version` https://docs.docker.com/compose/install/linux/ #### 1. ````yaml version: '3.9' services: hello: image: busybox entrypoint: /bin/echo 'Hello, World!' ```` Запуск, здесь и далее: `docker compose up` #### 2. из директории `cats_app` из прошлой лабы: Dockerfile взять из solutions.md оттуда ````yaml version: '3.9' services: cats_app: container_name: cats_app image: cats-app build: context: ../ dockerfile: ./cats_app/Dockerfile networks: - cats nginx: image: nginx container_name: nginx volumes: - ./nginx.conf:/etc/nginx/nginx.conf depends_on: - cats_app ports: - 5000:80 networks: - cats networks: cats: driver: bridge ```` #### 3. см. ниже #### 4. ````yaml version: '3.9' services: todo_api: image: todo-api:latest container_name: todo_api depends_on: - todo_postgres build: ./TodoApi environment: ConnectionStrings__PostgreSQL: "Host=todo_postgres;Port=5432;Database=todo;Username=todo;Password=todo" ASPNETCORE_URLS: 'http://*:80' restart: unless-stopped deploy: resources: limits: cpus: '1' memory: 500M networks: - todo todo_ui: image: todo-ui:latest container_name: todo_ui depends_on: - todo_api build: ./todo_ui restart: unless-stopped deploy: resources: limits: cpus: '1' memory: 300M ports: - 8080:80 networks: - todo todo_postgres: image: postgres:16 container_name: todo_postgres environment: POSTGRES_DB: "todo" POSTGRES_USER: "todo" POSTGRES_PASSWORD: "todo" PGDATA: "/var/lib/postgresql/data" volumes: - ./initdb:/docker-entrypoint-initdb.d - todo_pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U todo -d todo"] interval: 10s timeout: 5s retries: 5 start_period: 10s restart: unless-stopped deploy: resources: limits: cpus: '2' memory: 800M networks: - todo todo_pgadmin: container_name: todo_pgadmin image: dpage/pgadmin4:7 depends_on: - todo_postgres environment: PGADMIN_DEFAULT_EMAIL: "todo@example.com" PGADMIN_DEFAULT_PASSWORD: "todo" PGADMIN_CONFIG_SERVER_MODE: "False" volumes: - todo_admin_data:/var/lib/pgadmin ports: - "5050:80" restart: unless-stopped networks: - todo volumes: todo_pgdata: todo_admin_data: networks: todo: driver: bridge ```` ### 5. Docker Swarm #### 1. ````bash docker swarm init ```` Потом необходимо запомнить токен #### 2. ````bash docker node ls ```` #### 3. ````bash docker swarm join-token worker ```` #### 4. ````bash docker network create -d overlay todo_swarm ```` #### 5. ````bash docker volume create todo_swarm_pgdata --sharing onewriter ```` #### 6. ````bash docker service create --name=todo_postgres --replicas 1 --network=todo_swarm -e POSTGRES_DB=todo -e POSTGRES_USER=todo -e POSTGRES_PASSWORD=todo --mount type=bind,source=./initdb,target=/docker-entrypoint-initdb.d --mount type=volume,source=todo_swarm_pgdata,target=/var/lib/postgresql/data --health-cmd='pg_isready -U todo -d todo' --health-interval=10s --health-retries=5 --health-timeout=5s --health-start-period=10s postgres:16 ```` #### 7. ````bash docker service create --name=todo_api --replicas 1 --network=todo_swarm -e 'ConnectionStrings__PostgreSQL=Host=todo_postgres;Port=5432;Database=todo;Username=todo;Password=todo' -e 'ASPNETCORE_URLS=http://*:80' todo-api ```` #### 8. ````bash docker service create -p 8080:80 --name=todo_ui --replicas 1 --network=todo_swarm todo-ui ```` #### 9. Открыть localhost:8080 в браузере #### 10. ````bash docker service scale todo_ui=3 todo_api=3 ```` #### 11. ````bash docker service ps todo_ui ```` ````bash docker service ps todo_api ```` #### 12. ````bash docker service logs todo_api ```` #### 13. ````bash docker service rm todo_api && docker service rm todo_ui && docker service rm todo_postgreq ```` #### 14 ````yaml version: '3.9' services: todo_api: image: todo-api:latest depends_on: - todo_postgres environment: ConnectionStrings__PostgreSQL: "Host=todo_postgres;Port=5432;Database=todo;Username=todo;Password=todo" ASPNETCORE_URLS: 'http://*:80' deploy: mode: replicated replicas: 3 resources: limits: cpus: '1' memory: 500M networks: - todo todo_ui: image: todo-ui:latest depends_on: - todo_api deploy: mode: replicated replicas: 3 resources: limits: cpus: '1' memory: 300M ports: - 8080:80 networks: - todo todo_postgres: image: postgres:16 environment: POSTGRES_DB: "todo" POSTGRES_USER: "todo" POSTGRES_PASSWORD: "todo" PGDATA: "/var/lib/postgresql/data" volumes: - ./initdb:/docker-entrypoint-initdb.d - todo_pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U todo -d todo"] interval: 10s timeout: 5s retries: 5 start_period: 10s deploy: resources: limits: cpus: '2' memory: 800M networks: - todo todo_pgadmin: image: dpage/pgadmin4:7 depends_on: - todo_postgres environment: PGADMIN_DEFAULT_EMAIL: "todo@example.com" PGADMIN_DEFAULT_PASSWORD: "todo" PGADMIN_CONFIG_SERVER_MODE: "False" volumes: - todo_admin_data:/var/lib/pgadmin ports: - "5050:80" networks: - todo volumes: todo_pgdata: todo_admin_data: networks: todo: driver: overlay ```` #### 15. ````bash docker stack deploy --compose-file docker-compose.yml ````