Упростите свою веб-разработку с помощью 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
В каталоге у вас должны быть следующие файлы
- main.py
- Dockerfile
- pipfile
- 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.