第一章 需要了解的概念

1. Docker 是什么? 做什么用的?

2. 镜像的概念

3. 容器的概念

什么是沙箱机制

  1. 沙箱是一个虚拟系统程序,沙箱提供的环境相对于每一个运行的程序都是独立的,而且不会对现有的系统产生影响
  2. 沙箱具有非常良好的独立性, 隔离性, 所以能够搭建一些具有高风险的软件进行测试.
  3. 在沙箱里面运行病毒可以说也是安全操作.

docker rkt 还有什么 docker干什么用的_Dockerfile

Docker 是什么

Docker 是一个开源的应用容器引擎, 基于 GO语言 并遵从 Apache2.0 协议开源.

Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级,可移植的 容器 中, 然后发布到任何流行的 Linux 机器上, 也可以实现虚拟化.

容器 是完全使用 沙箱机制, 相互之间不会有任何接口(类似 iPhone 的 app), 更重要的是容器性能开销极低.

容器

虚拟机

docker rkt 还有什么 docker干什么用的_docker_02

Docker 容器优势

  1. 启动快
  2. 占用资源少

镜像

通过镜像创建容器
光盘, 装系统
镜像里面的内容

仓库

镜像的集中存放地


第二章 基本操作-镜像

1. 镜像的获取

  1. 从仓库获取镜像

搜索镜像: docker search image_name

  • 搜索结果过滤:

是否是官方: docker search --filter "is-official=true" image_name

是否是自动化构建: docker search --filter "is-automated=true" image_name

大于多少颗星: docker search --filter stars=3 image_name

2. 镜像下载

docker pull images_name

3. 镜像的查看

本地镜像的查看: docker images

4. 镜像的删除

本地镜像的删除: docker rmi image_name

5. 镜像上传到docker hub

▶ docker image list
REPOSITORY            TAG       IMAGE ID       CREATED         SIZE
hello                 latest    4e2ff61a0ac6   2 hours ago     206MB

▶ docker tag hello zqphper/hello

▶ docker image list
REPOSITORY            TAG       IMAGE ID       CREATED         SIZE
hello                 latest    4e2ff61a0ac6   2 hours ago     206MB
zqphper/hello         latest    4e2ff61a0ac6   2 hours ago     206MB

▶ docker push zqphper/hello

PS: docker tag hello zqphper/hello 一定要把前缀改成自己 docker hub 名称。


第三章 基础操作-容器

1. 创建容器

docker run -itd --name=container_name image_name

可以分开如下写

docker run -i -t -d --name=container_name image_name

-i 表示以交互模式运行容器;
-d 表示后台运行容器, 并返回容器 ID;
-t 为容器重新分配一个伪输入终端
–name 为容器指定名称,自定义名称
image_name 镜像的名字

2. 查看容器

  • 查看容器(运行中的)

docker ps

  • 查看容器(包括已停止的)

docker ps -a

3. 停止容器

docker stop container_name/container_id

container_name/container_id 意思是,填入 container_name 或者 container_id 任选其一即可. 以下类似.

4. 启动容器

docker start container_name/container_id

5. 重启容器

docker restart container_name/container_id

6. 删除容器

删除容器之前必须先停止容器

docker rm container_name/container_id


第四章 容器的修改及保存

1. 进入容器

docker exec -it container_name/container_id /bin/bash

2. 退出容器

exit

3. 修改容器

每次修改容器中的内容,一旦删除了,就没有了,所以修改完了,需要提交修改,此时,会生成新的镜像,之后可以通过这个新的镜像来生成新的容器。

docker commit -a "author" -m "message" container_name/container_id new_image_name:tag_name

  • 参数说明
  • -a 参数可选,用于指定作者,可以写你的名字
  • -m 参数可选,提交信息,可以说一下你做了哪些修改
  • container_id 该参数为被修改的容器ID
  • new_image_name 此为新镜像的名字,可自定义
  • tag_name 此为新镜像的标签, 可不写,不写时候标签默认为 latest

4. 保存修改


第五章 容器操作进阶

1. 端口映射

docker run -itd -p 宿主机端口号:容器端口号 --name=自定义的容器名字 images_name/images_id

上述命令就是 新建了一个容器, 在新建的同时对端口做了映射.

docker rkt 还有什么 docker干什么用的_mysql_03

2. 文件挂载

docker run -itd -v /宿主机/文件目录/文件名:/容器/目录/文件名

3. 将容器的文件复制到本地

docker cp 容器名:/容器目录/文件名 /宿主机目录/文件名

4. 将本地的文件复制到容器

docker cp /宿主机目录/文件名 容器名:/容器目录/文件名

5. 容器互联

docker run -itd --link 要关联的容器名字:容器在被关联的容器中的别名

docker-php-ext-install xxx php安装 xxx 插件

6. 修改mysql 密码

ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456'


第六章 Dockerfile

1. 什么是Dockerfile

Dockerfile 就是名为Dockerfile的文件,文件中包含一些Linux命令,Docker通过读取文件中的命令来组建镜像。

2. Dockerfile 文件内容结构

Dockerfile一般分为四部分: *基础镜像信息、维护者信息、镜像操作指定和容器启动时执行命令, **#**为Dockerfile中的注释

3. 运行 Dockerfile

docker build -t image_name:tag_name .(当前文件夹下)

也可以通过 -f 参数来指定 Dockerfile 文件位置

docker build -f /path/Dockerfile -t image_name:tag_name .(当前文件夹下)

4. 命令详解

FORM:指定基础镜像(一般为操作系统),必须为第一个命令

格式:
	FROM <image>
	FROM <image>:<tag>
	FROM <image>@<digext>
实例:
	FROM centos:7.0

MAINTAINER: 维护者信息

格式:
	MAINTAINER <name>
实例:
	MAINTAINER zhangsan

RUN: 构建镜像时执行的命令

格式:
	RUN <command>*exec执行*
格式:
	RUN ["executable", "param1", "param2"]
实例:
	RUN ["/bin/executable", "param1", "param2"]
	RUN yun install nginx

ADD: 将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget

格式:
	ADD <src>... <dest>
	ADD ["<src>",... "<dest>"] 用于支持包含空格的路径
格式:
	ADD tes* /mydir/	# 添加所有以“tes”开头的文件
	ADD tes?.txt /mydir/	# ? 替代一个单字符,例如:“test.txt”
	ADD test relativeDir/   # 添加“test"到 WORKDIR/relatieDir/
	ADD test /absoluterDir/  # 添加 “test" 到 /absoluteDir/ 
	
注:第一个参数指宿主机文件路径,第二个参数指容器路径

COPY: 功能类似ADD,但是不会自动解压文件,也不能访问网络资源
CMD: 构建容器后调用,也就是在容器启动时才进行调用

格式:
	CMD ["executable","param1","param2"] (执行可执行文件,优先)
	CMD ["param1","param2"] (设置了ENTRYPOINT, 则直接调用ENTRYPOINT添加参数)
	CMD command param1 param2 (执行shell内部命令)
实例:
	CMD echo "This is a test." | wc -
	CMD ["/usr/bin/wc", "--help"]
注:
	CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。

ENTRYPOINT: 配置容器,使其可执行化。配合CMD可省去”application“, 只使用其参数

格式:
	ENTRYPOINT ["exectable", "param1", "param2"] (可执行文件,优先)
	ENTRYPOINT command param1 param2 (shell内部文件)
实例:
	FROM ubuntu
	ENTRYPOINT ["top", "-b"]
	CMD ["-C"]
注:
	ENTRYPOINT 于 CMD 非常类似,不用的是通过docker run执行的命令不会覆盖 ENTRYPOINT, 而docker run命令中指定的任何参数,都会被当做参数再次传递给ENTRYPOINT.Dockerfile中只允许有一个ENTRYPOINT命令,多指定时会覆盖前面的设置,而只执行最后的ENTRYPOINT指令。
	docker run -itd --name=nginx nginx echo 'hello world'

LABEL: 用于为镜像添加元数据

格式:
	LABEL <key>=<value> <key>=<value> <key>=<value> ...
实例:
	LABEL version="1.0" description="这是一个nginx镜像”
注:
	使用LABEL指定元数据时,一条LABEL指令可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分割。推荐将所有的原数据通过一条LABEL指令指定,以免生成过多的中间镜像。

ENV: 设置环境变量

格式:
	ENV <key><value> #<key>之后的所有内容均会被视为其<value>的组成部分,因此,一次只能设置一个变量
	ENV <key>=<value> ... # 可以设置多个变量,每个变量为一个 “<key>=<value>”的键值对,如果<key>中包含空格,可以使用\来进行转义,也可以通过 “”来进行标示;另外,反斜线也可以用于续行

实例:
	ENV myName John Doe
	ENV myDog Rex The Dog
	ENV myCat=fluffy

5. 技巧

  1. 一些经常变动的文件,放在 dockerfile 文件的后面,因为遇到变动,往下的都不会使用缓存
  2. 添加 .dockerigonre 文件,来忽略不需要的文件
  3. 数据的持久化方案一 data volume -v 后,docker 帮我们存储的数据。容器删除,这个数据还在。只需要 -v 指定。例如 docker container run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d -v mysql-data:/var/lib/mysql mysql:5.7
▶ docker volume ls
DRIVER    VOLUME NAME
local     mysql-data
  1. 查看 data volume 命令:查看 docker volume ls 删除: docker volume rm xx, 查看路径 docker volume inspect xx
▶ docker volume inspect mysql-data
	[
	    {
	        "CreatedAt": "2022-04-20T09:15:04Z",
	        "Driver": "local",
	        "Labels": null,
	        "Mountpoint": "/var/lib/docker/volumes/mysql-data/_data",
	        "Name": "mysql-data",
	        "Options": null,
	        "Scope": "local"
	    }
	]
  1. 数据的持久化方案二,文件挂载 docker container run -d -v $(pwd):/app my-cron. -v 后面改成 本地路径:容器内路径。这里的 $(pwd) 代表当前文件夹路径。windows 为${pwd}

第七章 docker 网络

1. 容器为什么能够获取到IP地址?

因为都连接到了一个叫 docker0 的Linux bridge上

▶ docker network ls
NETWORK ID     NAME                 DRIVER    SCOPE
8b5056168c45   02-mysql56_default   bridge    local
c174f557191f   02-mysql57_default   bridge    local
b93f6238137b   _default             bridge    local
c95b7cff2474   bridge               bridge    local
63bfa5c00515   downloads_default    bridge    local
8a501831988d   host                 host      local
81307470d1e6   none                 null      local

▶ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "c95b7cff2474eb97b6c331364b86d5f8fd1e5585bff575ce03ce874945cd6dbc",
        "Created": "2022-04-19T08:43:42.982079922Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "42917487a6ffcb6b5bd2debcb5ada7cb78d04302443edb57b07bb95b08608803": {
                "Name": "box2",
                "EndpointID": "a64b7c2d2d5129f48809a76d88f66823bf91c723e16644e38d419f37e2ad4664",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "d23e2e27d87afa74b7aedd11e71cc1ee274a6602ce17e8db3a1c60cb82cd7a22": {
                "Name": "box1",
                "EndpointID": "6b497555b8d703ce5f7c567724f4d1f0c5dc57bb86f867320d372825cfcfc871",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

2. 为什么宿主机可以ping通容器的IP?

docker rkt 还有什么 docker干什么用的_mysql_04

容器 test1test2 都连接到 docker0,所以互通。再通过 eth0 连接到外网。

3. 为什么容器之间的IP是互通的?

因为不同容器之间,都是默认连在 docker0 下。所以是互通的。

4. 为什么容器能ping通外网?

因为 容器连到 docker0 下,docker0 通过 eth0 (宿主机网络)连接到 外网。

5. 容器的端口转发是怎么回事?

6. 创建和使用自定义 bridge 网络

  1. 创建
    docker network create -d bridge mybridge
  2. 查看
▶ docker network ls
NETWORK ID     NAME                 DRIVER    SCOPE
8b5056168c45   02-mysql56_default   bridge    local
c174f557191f   02-mysql57_default   bridge    local
b93f6238137b   _default             bridge    local
c95b7cff2474   bridge               bridge    local
63bfa5c00515   downloads_default    bridge    local
8a501831988d   host                 host      local
bb7ad4c0e970   mybridge             bridge    local
81307470d1e6   none                 null      local

▶ docker network inspect mybridge
[
    {
        "Name": "mybridge",
        "Id": "bb7ad4c0e970ca29441a76e26eb0503fccf1d544aa8ebe6d45b23114fbf7e1cf",
        "Created": "2022-04-21T02:35:51.811271Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.22.0.0/16",
                    "Gateway": "172.22.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]
  1. 使用
    docker container run -d --rm --name box3 --network mybridge busybox /bin/sh -c "while true;do sleep 3600;done" , 通过 --network mybridge 来指定。因为默认是链接到 docker0, 如果指定了,那么和 docker0 下的容器是不互通的。
  2. box3 容器连接的是 mybridge,现在让 box3 也连接默认的 bridge
▶ docker network connect bridge box3

▶ docker container inspect box3
[
    {
        "Id": "f7674477529e7da8b5c9eab0123d9809634914132f44f6d35ee94cf608513831",
        "Created": "2022-04-21T02:44:08.7924526Z",
        "Path": "/bin/sh",
        "Args": [
            "-c",
            "while true;do sleep 3600;done"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 21195,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2022-04-21T02:44:09.1457453Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:1a80408de790c0b1075d0a7e23ff7da78b311f85f36ea10098e4a6184c200964",
       	.
       	.
       	.
        "NetworkSettings": {
       		.
       		.
       		.
            "Networks": {
                "bridge": {
                    "IPAMConfig": {},
                    "Links": null,
                    "Aliases": [],
                    "NetworkID": "c95b7cff2474eb97b6c331364b86d5f8fd1e5585bff575ce03ce874945cd6dbc",
                    "EndpointID": "ec49b7a469b7a7e4d103645465f5a314ea859c3f9a9af1b03a2a990961f3e4af",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.4",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:04",
                    "DriverOpts": {}
                },
                "mybridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": [
                        "f7674477529e"
                    ],
                    "NetworkID": "bb7ad4c0e970ca29441a76e26eb0503fccf1d544aa8ebe6d45b23114fbf7e1cf",
                    "EndpointID": "91ce461b5850b79a178acab2bd1840b1030333f1bf233e1bac10af1b9eb82aae",
                    "Gateway": "172.22.0.1",
                    "IPAddress": "172.22.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:16:00:02",
                    "DriverOpts": null
                }
            }
        }
    }
]
  1. 移除网络
    docker network disconnect bridge box3

7. host 网络

▶ docker network ls
NETWORK ID     NAME                 DRIVER    SCOPE
c95b7cff2474   bridge               bridge    local
8a501831988d   host                 host      local
81307470d1e6   none                 null      local

默认会有3个网络

  1. bridge 新建容器,如果不指定,会使用这个网络。端口转发需要通过 -p 宿主机端口:容器内端口 来指定。
  2. host 网络,如果指定 --network host,这个网络的区别是,默认使用宿主机网络,意味着所有的端口,不需要指定,可以直接监听。
  3. none 网络,这个是一个 断网的网络,没有任何网络连接。

第八章 docker-compose

1. 什么是 docker-compose

快速启动多个容器的方法

version: "3.8"

services:
  flask-demo:
    container_name: my-flask-demo
    image: flask-demo:latest
    environment:
      - REDIS_HOST=redis-server
    networks:
      - demo-network
    ports:
      - 8080:5000

  redis-server:
    image: redis:latest
    networks:
     - demo-network

networks:
  demo-network:
  1. 启动 docker-compose up -d
  2. 关闭 docker-compose stop
  3. 移除 docker-compose down
  4. 命令帮助 docker-compose

如果 镜像不存在,则自动去拉取。

2. 如果docker-compose 中的镜像不存在,可以自己指定 Dockerfile 构建。

docker rkt 还有什么 docker干什么用的_docker_05

version: "3.8"

services:
  flask-demo:
    build: ./flask
    image: flask-demo:latest
    container_name: my-flask-demo
    image: flask-demo:latest
    environment:
      - REDIS_HOST=redis-server
    networks:
      - demo-network
    ports:
      - 8080:5000

  redis-server:
    image: redis:latest
    networks:
     - demo-network

networks:
  demo-network:

命令: docker-compose build 就会自动去构建。

  1. 如上构建自定义镜像时,如果名称不为 Dockerfile ,可以自定义,如下 build 写法
version: "3.8"

services:
  flask-demo:
    build: 
      context: ./flask
      dockerfile: Dockerfile.dev
    image: flask-demo:latest
    container_name: my-flask-demo
    image: flask-demo:latest
    environment:
      - REDIS_HOST=redis-server
    networks:
      - demo-network
    ports:
      - 8080:5000

  redis-server:
    image: redis:latest
    networks:
     - demo-network

networks:
  demo-network:

3. 服务更新(镜像构建发生变化)

  • 第一种方法:手动的更新 docker-compose build
  • 第二种方法:docker-compose up -d --build 会去查看镜像是否有变化,如果有变化,则自动去 build
  • 如果 docker-compose.yml 内容发生变化,则需要重启 docker-compose restart

常用的命令

  • 删除不用的 services docker-compose up -d --remove-orphans
  • 重启 docker-compose restart

4. docker-compose 网络

docker-compose 中可以指定网络,如下

version: "3.8"

services:
  box1:
    image: xiaopeng163/net-box:latest
    command: /bin/sh -c "while true;do sleep 3600; done"
    networks:
      - mynetwork1

  box2:
    image: xiaopeng163/net-box:latest
    command: /bin/sh -c "while true;do sleep 3600; done"
    networks:
      - mynetwork1
      - mynetwork2

networks:
  mynetwork1:
  mynetwork2:

5. 水平扩展

version: "3.8"

services:
  flask:
    build:
      context: ./flask
      dockerfile: Dockerfile
    image: flask-demo:latest
    environment:
      - REDIS_HOST=redis-server

  redis-server:
    image: redis:latest

  client:
    image: xiaopeng163/net-box:latest
    command: sh -c "while true; do sleep 3600; done;"

水平扩展:在 docker-compose.yml 中快速增加 services 的数量。
命令: docker-compose up -d --scale flask=3 flask 是service 的名字,3是数量。
执行完上面,再执行 docker-compose up -d --scale flask=1 会删除上面为我们新增的2个。

负载均衡:
如上, 水平扩展之后,会生成几个容器,然后在其中一个容器中,ping flask 会有不同的结果。因为 docker 默认做好了负载均衡。

6. docker-compose 环境变量

可以通过 .env 文件来指定环境变量

version: "3.8"

services:
  flask:
    build:
      context: ./flask
      dockerfile: Dockerfile
    image: flask-demo:latest
    environment:
      - REDIS_HOST=redis-server
      - REDIS_PASS=${REDIS_PASSWORD}
    networks:
      - backend
      - frontend

${REDIS_PASSWORD}.env 文件中

REDIS_PASSWORD=ABC123

可以通过命令 dokcer-compose config 来查看配置文件

▶ docker-compose config
networks:
  backend: {}
  frontend: {}
services:
  flask:
    build:
      context: /Users/zqmac/Desktop/02-daywork/2022/04/0422/compose-env/flask
      dockerfile: Dockerfile
    environment:
      REDIS_HOST: redis-server
      REDIS_PASS: ABC123
    image: flask-demo:latest
    networks:
      backend: null
      frontend: null
  nginx:
    depends_on:
      flask:
        condition: service_started
    image: nginx:stable-alpine
    networks:
      frontend: null
    ports:
    - published: 8000
      target: 80
    volumes:
    - /Users/zqmac/Desktop/02-daywork/2022/04/0422/compose-env/nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
    - /Users/zqmac/Desktop/02-daywork/2022/04/0422/compose-env/var/log/nginx:/var/log/nginx:rw
  redis-server:
    command: redis-server --requirepass ABC123
    image: redis:latest
    networks:
      backend: null
version: '3.8'

7. 服务依赖

一个 docker-compose.yml 里面有很多服务,所谓依赖,就是服务之间有先后顺序。用depends_on来指定

version: "3.8"

services:
  flask:
    build:
      context: ./flask
      dockerfile: Dockerfile
    image: flask-demo:latest
    environment:
      - REDIS_HOST=redis-server
    networks:
      - backend
      - frontend

  redis-server:
    image: redis:latest
    networks:
      - backend

  nginx:
    image: nginx:stable-alpine
    ports:
      - 8000:80
    depends_on: # 启动顺序,这里指的是 flask 启动完成之后,再启动 nginx
      - flask
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./var/log/nginx:/var/log/nginx
    networks:
      - frontend

networks:
  backend:
  frontend:

8. 健康检查

如上,一个 docker-compose.yml 中有很多服务,可以通过 服务依赖 ,来指定顺序执行。但有可能 服务起来了,但这个服务没有对外提供服务。例如 数据库服务,启动了,但是不提供服务。此时,就需要用到 健康检查。

version: "3.8"

services:
  flask:
    build:
      context: ./flask
      dockerfile: Dockerfile
    image: flask-demo:latest
    environment:
      - REDIS_HOST=redis-server
      - REDIS_PASS=${REDIS_PASSWORD}
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 40s
    depends_on:
      redis-server:
        condition: service_healthy
    networks:
      - backend
      - frontend

  redis-server:
    image: redis:latest
    command: redis-server --requirepass ${REDIS_PASSWORD}
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 1s
      timeout: 3s
      retries: 30
    networks:
      - backend

  nginx:
    image: nginx:stable-alpine
    ports:
      - 8000:80
    depends_on:
      flask:
        condition: service_healthy
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./var/log/nginx:/var/log/nginx
    networks:
      - frontend

networks:
  backend:
  frontend: