
docker compose环境变量
为增加安全性,在前面的python例子中增加redis的密码校验,密码从环境变量中获取:
from flask import Flask
from redis import StrictRedis
import os
import socket
app = Flask(__name__)
redis = StrictRedis(host=os.environ.get('REDIS_HOST', '127.0.0.1'),
                    port=6379, password=os.environ.get('REDIS_PASS'))
@app.route('/')
def hello():
    redis.incr('hits')
    return f"Hello Container World! I have been seen {redis.get('hits').decode('utf-8')} times and my hostname is {socket.gethostname()}.\n"
对前面的docker-compose.yml进行修改如下:
version: "3.3"
services:
  flask-demo:
    build:
        context: .
        dockerfile: Dockerfile
    image: flask-demo:latest
    environment:
      - REDIS_HOST=redis-server
      - REDIS_PASS=${REDIS_PASSWORD}
    networks:
      - demo-network
    ports:
      - 8080:5000
  redis-server:
    image: redis:latest
    command: redis-server --requirepass ${REDIS_PASSWORD}
    networks:
     - demo-network
networks:
  demo-network:
在工程目录下新建.env文件,将环境变量配置好:
REDIS_PASS=abc
启动服务:
$ docker-compose up -d
Creating network "app3_demo-network" with the default driver
Creating app3_redis-server_1 ... done
Creating app3_flask-demo_1   ... done
参考文档:https://docs.docker.com/compose/environment-variables/
docker compose健康检查
Dockerfile healthcheck: https://docs.docker.com/engine/reference/builder/#healthcheck
docker compose:https://docs.docker.com/compose/compose-file/compose-file-v3/#healthcheck
健康检查是容器运行状态的高级检查,主要是检查容器所运行的进程是否能正常的对外提供“服务”,比如一个数据库容器,我们不光需要这个容器是up的状态,我们还要求这个容器的数据库进程能够正常对外提供服务,这就是所谓的健康检查。
容器的健康检查
容器本身有一个健康检查的功能,但是需要在Dockerfile里定义,或者在执行docker container run的时候,通过下面的一些参数指定:
--health-cmd string              Command to run to check health
--health-interval duration       Time between running the check
                                (ms|s|m|h) (default 0s)
--health-retries int             Consecutive failures needed to
                                report unhealthy
--health-start-period duration   Start period for the container to
                                initialize before starting
                                health-retries countdown
                                (ms|s|m|h) (default 0s)
--health-timeout duration        Maximum time to allow one check to
下面我们对前面的flask例子增加健康检查,主要是在Dockerfile中增加HEALTHCHECK指令:
FROM python:3.9.5-slim
RUN pip install flask redis && \
    apt-get update && \
    apt-get install -y curl && \
    groupadd -r flask && useradd -r -g flask flask && \
    mkdir /src && \
    chown -R flask:flask /src
USER flask
COPY app.py /src/app.py
WORKDIR /src
ENV FLASK_APP=app.py REDIS_HOST=redis
EXPOSE 5000
HEALTHCHECK --interval=30s --timeout=3s \
    CMD curl -f http://localhost:5000/ || exit 1
CMD ["flask", "run", "-h", "0.0.0.0"]
上面Dockerfili里的HEALTHCHECK就是定义了一个健康检查。会每隔30秒检查一次,如果失败就会退出,退出代码是1。
构建flask-demo镜像,并启动容器:
$ docker build -t flask-demo .
$ docker network create mynetwork
b3958a0cb961ddcc856c7e0458bc10489c456727f6061d1f9dc23e7e264741ae
$ docker container run --rm -d --name flask-demo --network=mynetwork --env REDIS_PASS=abc --env REDIS_HOST=redis flask-demo
e761d19949b9e3284471864101ec196807fc6145431903f4041e04daf950fa86
$ docker container ps
CONTAINER ID   IMAGE        COMMAND                  CREATED         STATUS                            PORTS      NAMES
e761d19949b9   flask-demo   "flask run -h 0.0.0.0"   8 seconds ago   Up 8 seconds (health: starting)   5000/tcp   flask-demo
启动容器后查看容器状态未health: starting。
因为此时没有启动redis服务,所以无法访问http://localhost:5000/,经过3次检查后发现一直是不通的,然后health的状态会从starting变为unhealthy:
$ docker container ps
CONTAINER ID   IMAGE        COMMAND                  CREATED         STATUS                     PORTS      NAMES
e761d19949b9   flask-demo   "flask run -h 0.0.0.0"   2 minutes ago   Up 2 minutes (unhealthy)   5000/tcp   flask-demo
也可以通过docker container inspect查看其中有关health的详情:
"Health": {
    "Status": "unhealthy",
    "FailingStreak": 4,
    "Log": [
        {
            "Start": "2023-10-09T14:17:51.7014803+08:00",
            "End": "2023-10-09T14:17:54.7023591+08:00",
            "ExitCode": -1,
            "Output": "Health check exceeded timeout (3s)"
        },
        {
            "Start": "2023-10-09T14:18:24.7095286+08:00",
            "End": "2023-10-09T14:18:27.7099692+08:00",
            "ExitCode": -1,
            "Output": "Health check exceeded timeout (3s)"
        },
        {
            "Start": "2023-10-09T14:18:57.7264309+08:00",
            "End": "2023-10-09T14:19:00.7267289+08:00",
            "ExitCode": -1,
            "Output": "Health check exceeded timeout (3s)"
        },
        {
            "Start": "2023-10-09T14:19:30.7443718+08:00",
            "End": "2023-10-09T14:19:33.7444952+08:00",
            "ExitCode": -1,
            "Output": "Health check exceeded timeout (3s)"
        }
    ]
}
此时再启动redis服务,注意设置访问redis的密码:
$ docker container run --rm -d --network=mynetwork --name redis redis redis-server --requirepass abc
985cd32adbffd0d631a5a193c25903d704ddf08fb9a06f65a8e05af601a2ad77
$ docker container ps
CONTAINER ID   IMAGE        COMMAND                  CREATED              STATUS                   PORTS      NAMES
985cd32adbff   redis        "docker-entrypoint.s…"   About a minute ago   Up About a minute        6379/tcp   redis
e761d19949b9   flask-demo   "flask run -h 0.0.0.0"   8 minutes ago        Up 8 minutes (healthy)   5000/tcp   flask-demo
经过几秒钟,我们的flask-demo变成了healthy。
docker-compose健康检查
在上面的例子基础上删除Dockerfile中的心跳检测。
在docker-compose.yml中增加健康检查的配置:
version: "3.8"
services:
  flask-demo:
    build:
        context: .
        dockerfile: Dockerfile
    image: flask-demo:latest
    environment:
      - REDIS_HOST=redis-server
      - REDIS_PASS=${REDIS_PASS}
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 40s
    networks:
      - demo-network
    ports:
      - 8080:5000
  redis-server:
    image: redis:latest
    command: redis-server --requirepass ${REDIS_PASS}
    networks:
     - demo-network
networks:
  demo-network:
构建镜像并启动:
$ docker-compose up -d --build
$ docker-compose ps
       Name                      Command                  State                        Ports
--------------------------------------------------------------------------------------------------------------
app5_flask-demo_1     flask run -h 0.0.0.0             Up (healthy)   0.0.0.0:8080->5000/tcp,:::8080->5000/tcp
app5_redis-server_1   docker-entrypoint.sh redis ...   Up             6379/tcp
可以通过修改docker-compose.yml文件中密码来测试容器状态为Up (unhealthy)的情况。
docker compose服务依赖
如果服务之间的启动有依赖顺序,可以使用depends_on来配置。
version: "3.8"
services:
  flask-demo:
    build:
        context: .
        dockerfile: Dockerfile
    image: flask-demo:latest
    environment:
      - REDIS_HOST=redis-server
      - REDIS_PASS=${REDIS_PASS}
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 40s
    depends_on:
      - redis-server
    networks:
      - demo-network
    ports:
      - 8080:5000
  redis-server:
    image: redis:latest
    command: redis-server --requirepass ${REDIS_PASS}
    networks:
     - demo-network
networks:
  demo-network:
上面的例子配置了flask-demo依赖redis-server,在启动的时候会先启动redis-server,然后在启动flask-demo:
$ docker-compose up -d
Creating network "app6_demo-network" with the default driver
Creating app6_redis-server_1 ... done
Creating app6_flask-demo_1   ... done
服务依赖与健康检查的结合
这里再引入nginx来更好的演示服务依赖与健康检查的结合。
version: "3.8"
services:
  flask-demo:
    build:
        context: .
        dockerfile: Dockerfile
    image: flask-demo:latest
    environment:
      - REDIS_HOST=redis-server
      - REDIS_PASS=${REDIS_PASS}
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 40s
    depends_on:
      - redis-server
    networks:
      - backend
      - frontend
  redis-server:
    image: redis:latest
    command: redis-server --requirepass ${REDIS_PASS}
    networks:
      - backend
  nginx:
    image: nginx:stable-alpine
    ports:
      - 8000:80
    depends_on:
      flask-demo:
        condition: service_healthy
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./log/nginx:/var/log/nginx
    networks:
      - frontend
networks:
  backend:
  frontend:
nginx.conf文件的内容如下:
server {
  listen  80 default_server;
  location / {
    proxy_pass http://flask-demo:5000;
  }
}
启动服务,可以发现nginx在flask-demo启动后并健康检查通过后才启动。
$ docker-compose up -d
Creating network "app7_backend" with the default driver
Creating network "app7_frontend" with the default driver
Creating app7_redis-server_1 ... done
Creating app7_flask-demo_1   ... done
Creating app7_nginx_1        ... done
$ docker-compose ps
       Name                      Command                  State                      Ports
----------------------------------------------------------------------------------------------------------
app7_flask-demo_1     flask run -h 0.0.0.0             Up (healthy)   5000/tcp
app7_nginx_1          /docker-entrypoint.sh ngin ...   Up             0.0.0.0:8000->80/tcp,:::8000->80/tcp
app7_redis-server_1   docker-entrypoint.sh redis ...   Up             6379/tcp



















