Python flask应用程序在swarm中访问docker机密

Python flask应用程序在swarm中访问docker机密,python,docker-compose,dockerfile,docker-swarm,Python,Docker Compose,Dockerfile,Docker Swarm,我想部署一个flask+gunicorn项目,我是Docker的新手。到目前为止,我有一个Dockerfile如下 # Pull official base image FROM python:3.7-slim-buster # Set work directory RUN mkdir -p /usr/src/app WORKDIR /usr/src/app # Set environment variables ENV REDIS_HOST [...omit here...] ENV RE

我想部署一个flask+gunicorn项目,我是Docker的新手。到目前为止,我有一个Dockerfile如下

# Pull official base image
FROM python:3.7-slim-buster

# Set work directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# Set environment variables
ENV REDIS_HOST [...omit here...]
ENV REDIS_PORT [...omit here...]
ENV REDIS_DB_WHITELIST [...omit here...]
ENV MYSQL_HOST [...omit here...]
ENV MYSQL_PORT [...omit here...]
ENV MYSQL_DB_DUMMY [...omit here...]

# Copy project
COPY . /usr/src/app/

# Install dependencies
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
RUN pip install gunicorn

EXPOSE 5000

RUN chmod +x ./entrypoint.sh
ENTRYPOINT ["sh", "entrypoint.sh"]
version: "3.9"

secrets:
  FLASK_SECRET_KEY:
    external: true
  MYSQL_USER:
    external: true
  MYSQL_PASSWORD:
    external: true

services:
  web:
    image: flask-app:v0.1.0
    environment:
      FLASK_SECRET_KEY_FILE: /run/secrets/FLASK_SECRET_KEY
      MYSQL_USER_FILE: /run/secrets/MYSQL_USER
      MYSQL_PASSWORD_FILE: /run/secrets/MYSQL_PASSWORD
    ports:
      - "5000:5000"
    secrets:
      - FLASK_SECRET_KEY
      - MYSQL_USER
      - MYSQL_PASSWORD
version: "3.9"

services:
  web:
    ...
    secrets:
      - amqp_user
      - amqp_password
    ...

secrets:
  amqp_user:
    external: true
  amqp_password:
    external: true
和docker-compose.yml,如下所示

# Pull official base image
FROM python:3.7-slim-buster

# Set work directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# Set environment variables
ENV REDIS_HOST [...omit here...]
ENV REDIS_PORT [...omit here...]
ENV REDIS_DB_WHITELIST [...omit here...]
ENV MYSQL_HOST [...omit here...]
ENV MYSQL_PORT [...omit here...]
ENV MYSQL_DB_DUMMY [...omit here...]

# Copy project
COPY . /usr/src/app/

# Install dependencies
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
RUN pip install gunicorn

EXPOSE 5000

RUN chmod +x ./entrypoint.sh
ENTRYPOINT ["sh", "entrypoint.sh"]
version: "3.9"

secrets:
  FLASK_SECRET_KEY:
    external: true
  MYSQL_USER:
    external: true
  MYSQL_PASSWORD:
    external: true

services:
  web:
    image: flask-app:v0.1.0
    environment:
      FLASK_SECRET_KEY_FILE: /run/secrets/FLASK_SECRET_KEY
      MYSQL_USER_FILE: /run/secrets/MYSQL_USER
      MYSQL_PASSWORD_FILE: /run/secrets/MYSQL_PASSWORD
    ports:
      - "5000:5000"
    secrets:
      - FLASK_SECRET_KEY
      - MYSQL_USER
      - MYSQL_PASSWORD
version: "3.9"

services:
  web:
    ...
    secrets:
      - amqp_user
      - amqp_password
    ...

secrets:
  amqp_user:
    external: true
  amqp_password:
    external: true
在我通过谷歌搜索之后,似乎访问docker机密的唯一方法是使用
docker stack deploy--compose file=docker-compose.yml flask app
命令。显然,我有三个敏感的数据密钥,MYSQL用户,MYSQL密码需要存储在Docker secrets中。事实证明,应用程序一直无法运行,我假设python脚本中的
mysql\u user=os.environ['mysql\u user']
等无法访问环境变量


我不知道通过Dockerfile或Docker-compose.yml从Docker secrets访问敏感数据的正确方法,如果我出错,请纠正我。

您使用了简短的语法来声明服务上的机密。默认情况下,此类机密将装入容器内的/run/secrets/{secretname}

长语法甚至允许指定目标位置(尽管v3 compose引用声称它只装载到/run/secrets/):

创建一个秘密:

echo "mysecret" | docker secret create mysecret -
注意:这个秘密可能是在docker-compose.yml中创建的,同时还带有一个文件引用。为了简单起见,我选择在我的示例中手动创建它

在群堆中消耗秘密:

version: '3.8'
services:
  testsecret:
    image: ubuntu
    deploy:
      replicas: 1
    tty: true
    secrets:
      - source: mysecret
        target: /path/in/container/mysecret
        mode: 0444

secrets:
  mysecret:
    external: true
然后读取应用程序中的文件/path/in/container/mysecret以获取其内容

请记住,秘密总是以只读方式装载的。增加的安全性是,秘密在swarm节点之间加密分配,并加密存储在raft日志中(~=集群状态)。一旦秘密装入容器中,它将是tempfs上的未加密文件。另一个优点是细节不作为环境变量公开,因此不太可能被意外泄漏

docker compose似乎与v3.x版的compose文件规范存在定义上的差距,该规范允许在docker compose部署中使用机密。但是:许多低级功能届时将不可用


几天后,v3 compose file reference页面似乎被破坏了:从页眉和页脚看,整个描述都丢失了…

您使用了简短的语法来声明服务上的秘密。默认情况下,此类机密将装入容器内的/run/secrets/{secretname}

长语法甚至允许指定目标位置(尽管v3 compose引用声称它只装载到/run/secrets/):

创建一个秘密:

echo "mysecret" | docker secret create mysecret -
注意:这个秘密可能是在docker-compose.yml中创建的,同时还带有一个文件引用。为了简单起见,我选择在我的示例中手动创建它

在群堆中消耗秘密:

version: '3.8'
services:
  testsecret:
    image: ubuntu
    deploy:
      replicas: 1
    tty: true
    secrets:
      - source: mysecret
        target: /path/in/container/mysecret
        mode: 0444

secrets:
  mysecret:
    external: true
然后读取应用程序中的文件/path/in/container/mysecret以获取其内容

请记住,秘密总是以只读方式装载的。增加的安全性是,秘密在swarm节点之间加密分配,并加密存储在raft日志中(~=集群状态)。一旦秘密装入容器中,它将是tempfs上的未加密文件。另一个优点是细节不作为环境变量公开,因此不太可能被意外泄漏

docker compose似乎与v3.x版的compose文件规范存在定义上的差距,该规范允许在docker compose部署中使用机密。但是:许多低级功能届时将不可用


几天后,v3 compose file reference页面似乎被破坏了:从页眉和页脚可见,整个描述都丢失了…

我确实找到了一种从docker secret访问敏感数据的方法,使用。下面是我的项目级
config.py
模块的一些片段

import os
from dotenv import load_dotenv

dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path=dotenv_path)


def manage_sensitive(name):
    v1 = os.getenv(name)
    
    secret_fpath = f'/run/secrets/{name}'
    existence = os.path.exists(secret_fpath)
    
    if v1 is not None:
        return v1
    
    if existence:
        v2 = open(secret_fpath).read().rstrip('\n')
        return v2
    
    if all([v1 is None, not existence]):
        return KeyError(f'{name}')


class ConfigRabbitMQ:
    AMQP_USER = manage_sensitive(name='amqp_user')
    AMQP_PASSWORD = manage_sensitive(name='amqp_password')
    
    AMQP_HOST = manage_sensitive(name='amqp_host')
    AMQP_PORT = manage_sensitive(name='amqp_port')
因此,在这个
config.py
模块的同一目录中有一个
.env
文件。此模块可以访问Docker secret和
.env
文件中的敏感数据,因为通常在
.dockrignore
文件中列出
.env
。因此,例如,
docker compose.yml
如下所示

# Pull official base image
FROM python:3.7-slim-buster

# Set work directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# Set environment variables
ENV REDIS_HOST [...omit here...]
ENV REDIS_PORT [...omit here...]
ENV REDIS_DB_WHITELIST [...omit here...]
ENV MYSQL_HOST [...omit here...]
ENV MYSQL_PORT [...omit here...]
ENV MYSQL_DB_DUMMY [...omit here...]

# Copy project
COPY . /usr/src/app/

# Install dependencies
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
RUN pip install gunicorn

EXPOSE 5000

RUN chmod +x ./entrypoint.sh
ENTRYPOINT ["sh", "entrypoint.sh"]
version: "3.9"

secrets:
  FLASK_SECRET_KEY:
    external: true
  MYSQL_USER:
    external: true
  MYSQL_PASSWORD:
    external: true

services:
  web:
    image: flask-app:v0.1.0
    environment:
      FLASK_SECRET_KEY_FILE: /run/secrets/FLASK_SECRET_KEY
      MYSQL_USER_FILE: /run/secrets/MYSQL_USER
      MYSQL_PASSWORD_FILE: /run/secrets/MYSQL_PASSWORD
    ports:
      - "5000:5000"
    secrets:
      - FLASK_SECRET_KEY
      - MYSQL_USER
      - MYSQL_PASSWORD
version: "3.9"

services:
  web:
    ...
    secrets:
      - amqp_user
      - amqp_password
    ...

secrets:
  amqp_user:
    external: true
  amqp_password:
    external: true

对于更好的做法有什么建议吗?

我确实找到了一种使用docker secret访问敏感数据的方法。下面是我的项目级
config.py
模块的一些片段

import os
from dotenv import load_dotenv

dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path=dotenv_path)


def manage_sensitive(name):
    v1 = os.getenv(name)
    
    secret_fpath = f'/run/secrets/{name}'
    existence = os.path.exists(secret_fpath)
    
    if v1 is not None:
        return v1
    
    if existence:
        v2 = open(secret_fpath).read().rstrip('\n')
        return v2
    
    if all([v1 is None, not existence]):
        return KeyError(f'{name}')


class ConfigRabbitMQ:
    AMQP_USER = manage_sensitive(name='amqp_user')
    AMQP_PASSWORD = manage_sensitive(name='amqp_password')
    
    AMQP_HOST = manage_sensitive(name='amqp_host')
    AMQP_PORT = manage_sensitive(name='amqp_port')
因此,在这个
config.py
模块的同一目录中有一个
.env
文件。此模块可以访问Docker secret和
.env
文件中的敏感数据,因为通常在
.dockrignore
文件中列出
.env
。因此,例如,
docker compose.yml
如下所示

# Pull official base image
FROM python:3.7-slim-buster

# Set work directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# Set environment variables
ENV REDIS_HOST [...omit here...]
ENV REDIS_PORT [...omit here...]
ENV REDIS_DB_WHITELIST [...omit here...]
ENV MYSQL_HOST [...omit here...]
ENV MYSQL_PORT [...omit here...]
ENV MYSQL_DB_DUMMY [...omit here...]

# Copy project
COPY . /usr/src/app/

# Install dependencies
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
RUN pip install gunicorn

EXPOSE 5000

RUN chmod +x ./entrypoint.sh
ENTRYPOINT ["sh", "entrypoint.sh"]
version: "3.9"

secrets:
  FLASK_SECRET_KEY:
    external: true
  MYSQL_USER:
    external: true
  MYSQL_PASSWORD:
    external: true

services:
  web:
    image: flask-app:v0.1.0
    environment:
      FLASK_SECRET_KEY_FILE: /run/secrets/FLASK_SECRET_KEY
      MYSQL_USER_FILE: /run/secrets/MYSQL_USER
      MYSQL_PASSWORD_FILE: /run/secrets/MYSQL_PASSWORD
    ports:
      - "5000:5000"
    secrets:
      - FLASK_SECRET_KEY
      - MYSQL_USER
      - MYSQL_PASSWORD
version: "3.9"

services:
  web:
    ...
    secrets:
      - amqp_user
      - amqp_password
    ...

secrets:
  amqp_user:
    external: true
  amqp_password:
    external: true

有什么更好的建议吗?

对不起,我的答复可能过期了。正如您所看到的,我确实找到了Docker secret,并选择从其默认路径访问它。我仍然不确定这是否是一个好的做法。如果对此有任何建议,我将不胜感激。对不起,我的回复可能过期了。正如您所看到的,我确实找到了Docker secret,并选择从其默认路径访问它。我仍然不确定这是否是一个好的做法。如果对此有任何建议,我将不胜感激。