Упростите свою веб-разработку с помощью docker и docker-compose

вступление

В этой статье мы расскажем, как создать REST API на основе Python и использовать docker-compose для улучшения как разработки, так и производства. Однако прежде чем вы начнете читать эту статью, я бы порекомендовал понять, как использовать python с докером в части 1.

Docker Compose

Docker compose - это инструмент для управления несколькими контейнерами в одном файле YAML и управления этими контейнерами с помощью одной команды, что значительно упрощает нашу разработку.

В предыдущем туториале мы создали два контейнера: контейнер для Python REST API и контейнер для PostgreSQL-DB. Нам приходилось создавать образ для нашего API-интерфейса python каждый раз, когда мы меняли код, нам приходилось запускать каждый контейнер отдельно и вручную проверять, что этот контейнер базы данных запускается первым. Более того, нам нужно было заранее создать сеть, чтобы мы соединяли контейнеры, и мы должны были добавить эти контейнеры в эту сеть, и тогда мы назвали ее mynet. С docker-compose мы можем обо всем этом забыть.

Давайте посмотрим, как мы можем использовать docker-compose

В каталоге у вас должны быть следующие файлы

  1. main.py
  2. Dockerfile
  3. pipfile
  4. pipfile.lock

main.py

import uvicorn
from fastapi import FastAPI, Depends
from pydantic import BaseModel
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session
SQLALCHEMY_DATABASE_URL = "postgresql://postgres:postgres@postgres/postgres"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
class UserModel(Base):
    __tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
    first_name = Column(String, unique=True, index=True)
    last_name = Column(String)
    age = Column(Integer,)
Base.metadata.create_all(bind=engine)
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()
app = FastAPI()
class UserSchema(BaseModel):
    first_name: str
    last_name: str = None
    age: int
class Config:
        orm_mode = True
@app.post("/user/", response_model=UserSchema)
async def create_user(user: UserSchema, db: Session = Depends(get_db)):
    _user = UserModel(
        first_name=user.first_name, last_name=user.last_name, age=user.age
    )
    db.add(_user)
    db.commit()
    db.refresh(_user)
    return _user
@app.get("/user/", response_model=UserSchema)
async def get_user(first_name: str, db: Session = Depends(get_db)):
    _user = db.query(UserModel).filter_by(first_name=first_name).first()
    return _user
if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

Dockerfile

# Pull base image
FROM python:3.7
# Set environment varibles
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
WORKDIR /code/
# Install dependencies
RUN pip install pipenv
COPY Pipfile Pipfile.lock /code/
RUN pipenv install --system --dev
COPY . /code/
EXPOSE 8000
CMD ["python", "main.py"]

Теперь добавим docker-compose.yml файл

version: "3"
services:
  postgres:
    image: postgres:11
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=postgres
  web:
    build: .
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - postgres

Приведенная выше конфигурация создаст оба образа докеров и запустит оба контейнера с помощью одной команды.

$ docker-compose up --build

Как видите, docker-compose позаботился обо всем (сети, изображения, контейнеры и т. Д.). Но вы заметили следующее.

depends_on:
      - postgres

Это проверка работоспособности postgres, чтобы убедиться, что postgres запущен и работает, прежде чем мы запустим контейнер нашего приложения.

Теперь давайте добавим еще один контейнер для pgadmin4, который является инструментом управления графическим интерфейсом для postgres.

version: "3"
services:
  postgres:
    image: postgres:11
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=postgres
app:
    build: .
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - postgres
pgadmin:
    container_name: pgadmin
    image: dpage/pgadmin4
    environment:
      - [email protected]
      - PGADMIN_DEFAULT_PASSWORD=admin
    ports:
      - "5050:80"
    depends_on:
      - postgres

Проверить http://localhost:5050

Электронная почта и пароль такие же, как мы его установили в конфигурации

environment:
      - [email protected]
      - PGADMIN_DEFAULT_PASSWORD=admin

Имя пользователя, пароль, имя базы данных и порт такие же, как мы определили в конфигурации

environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=postgres

И давайте запустим запрос выбора

Иногда необходимо запустить только одну из служб (если она не зависит от другой службы), в нашем случае это база данных.

Мы можем сделать это так

$ docker-compose run postgres

Возможно, нам нужен доступ оболочки к контейнеру, возможно, нам нужно выполнить команду внутри, проверить журналы или файлы.

$ docker-compose run app bash

Возможно, нам нужно кеширование, ну давайте воспользуемся Redis.

version: "3"
services:
  postgres:
    image: postgres:11
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=postgres
app:
    build: .
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - postgres
      - redis
pgadmin:
    container_name: pgadmin
    image: dpage/pgadmin4
    environment:
      - [email protected]
      - PGADMIN_DEFAULT_PASSWORD=admin
    ports:
      - "5050:80"
    depends_on:
      - postgres
redis:
    image: "redis:alpine"
    ports:
      - "6379:6379"

и снова

$ docker-compose up --build

Docker compose - отличный инструмент для управления несколькими службами одновременно, с помощью нескольких строк YAML вы можете настроить всю инфраструктуру для своего приложения в одном файле. Однако есть недостаток: docker-compose работает на одном узле, что делает масштабирование сложным, ручным и очень ограниченным. Чтобы иметь возможность масштабировать сервисы на нескольких хостах / узлах, в игру вступают оркестраторы, такие как docker-swarm или kubernetes.

Полный код можно найти здесь на github.