Podman + Nginx + Affine 踩坑教程

安装 Podman

安装 podman 很简单

sudo apt install podman 

验证安装

podman info 

创建一个 pod 用于安装 Nginx 和 Affine

podman pod create --name mypod -p 80:8080 

注意:需要在/etc/sysctl.conf中加入net.ipv4.ip_unprivileged_port_start=80才能创建 80 端口映射

安装 Nginx

创建对应的文件夹

mkdir -p /opt/serves/nginx/app mkdir -p /opt/serves/nginx/conf mkdir -p /opt/serves/nginx/logs 

创建 bitnami 的 Nginx 容器

podman run -d --pod mypod --name nginx  -v /opt/serves/nginx/app:/app  -v /opt/serves/nginx/conf:/opt/bitnami/nginx/conf  -v /opt/serves/nginx/logs:/opt/bitnami/nginx/logs  bitnami/nginx:latest 

需要注意的点:

  • --pod mypod将容器加入 pod 中
  • 浏览器访问http://IP检查 Nginx 是否正常运行

安装 Affine

创建对应的文件夹

mkdir -p /opt/serves/affine/postgres/pgdata mkdir -p /opt/serves/affine/storage mkdir -p /opt/serves/affine/config 

通过 podman 命令安装 affine

虽然 Affine 官方提供了 docker-compose.yaml 文件,但是 podman comopse 无法直接将创建的容器加入 pod 中,因此需要用对应的命令逐个创建容器。

这是官方提供的 compose 和 env 文件,我们不使用它们,而是直接把环境变量和参数在命令行中设置好

  • docker-compose.yaml
name: affine services:   affine:     image: ghcr.io/toeverything/affine:${AFFINE_REVISION:-stable}     container_name: affine_server     # ports:     #   - '${PORT:-3010}:3010'     depends_on:       redis:         condition: service_healthy       postgres:         condition: service_healthy       affine_migration:         condition: service_completed_successfully     volumes:       # custom configurations       - ${UPLOAD_LOCATION}:/root/.affine/storage       - ${CONFIG_LOCATION}:/root/.affine/config     env_file:       - .env     environment:       - REDIS_SERVER_HOST=redis       - DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@postgres:5432/${DB_DATABASE:-affine}       - AFFINE_INDEXER_ENABLED=false     restart: unless-stopped    affine_migration:     image: ghcr.io/toeverything/affine:${AFFINE_REVISION:-stable}     container_name: affine_migration_job     volumes:       # custom configurations       - ${UPLOAD_LOCATION}:/root/.affine/storage       - ${CONFIG_LOCATION}:/root/.affine/config     command: ['sh', '-c', 'node ./scripts/self-host-predeploy.js']     env_file:       - .env     environment:       - REDIS_SERVER_HOST=redis       - DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@postgres:5432/${DB_DATABASE:-affine}       - AFFINE_INDEXER_ENABLED=false     depends_on:       postgres:         condition: service_healthy       redis:         condition: service_healthy    redis:     image: redis     container_name: affine_redis     healthcheck:       test: ['CMD', 'redis-cli', '--raw', 'incr', 'ping']       interval: 10s       timeout: 5s       retries: 5     restart: unless-stopped    postgres:     image: pgvector/pgvector:pg16     container_name: affine_postgres     volumes:       - ${DB_DATA_LOCATION}:/var/lib/postgresql/data     environment:       POSTGRES_USER: ${DB_USERNAME}       POSTGRES_PASSWORD: ${DB_PASSWORD}       POSTGRES_DB: ${DB_DATABASE:-affine}       POSTGRES_INITDB_ARGS: '--data-checksums'       # you better set a password for you database       # or you may add 'POSTGRES_HOST_AUTH_METHOD=trust' to ignore postgres security policy       POSTGRES_HOST_AUTH_METHOD: trust     healthcheck:       test:         ['CMD', 'pg_isready', '-U', "${DB_USERNAME}", '-d', "${DB_DATABASE:-affine}"]       interval: 10s       timeout: 5s       retries: 5     restart: unless-stopped  
  • .env
# select a revision to deploy, available values: stable, beta, canary AFFINE_REVISION=stable  # set the port for the server container it will expose the server on PORT=3010  # set the host for the server for outgoing links # AFFINE_SERVER_HTTPS=true # AFFINE_SERVER_HOST=affine.yourdomain.com # or # AFFINE_SERVER_EXTERNAL_URL=https://affine.yourdomain.com  # position of the database data to persist DB_DATA_LOCATION=/opt/serves/affine/postgres/pgdata # position of the upload data(images, files, etc.) to persist UPLOAD_LOCATION=/opt/serves/affine/storage # position of the configuration files to persist CONFIG_LOCATION=/opt/serves/affine/config  # database credentials DB_USERNAME=affine DB_PASSWORD=[你的密码] DB_DATABASE=affine 
创建 Redis 容器

通过观察compose文件,可以看出我们需要创建 Redis、Postgres、Affine Migration 和 Affine Server 四个容器,我们从 Redis 开始

Redis 对应的部分是:

  redis:     image: redis     container_name: affine_redis     healthcheck:       test: ['CMD', 'redis-cli', '--raw', 'incr', 'ping']       interval: 10s       timeout: 5s       retries: 5     restart: unless-stopped 

转换成 podman 命令就是:

podman run -d --pod mypod --name affine_redis  --restart unless-stopped  --health-cmd "redis-cli --raw incr ping"  --health-interval 10s  --health-timeout 5s  --health-retries 5 redis 

可以看到创建 Redis 容器还是比较简单的,没有什么需要配置的地方

创建 Postgres 容器

不需要等待 Redis 运行,继续创建 Postgres 容器

Postgres 对应的部分是:

  postgres:     image: pgvector/pgvector:pg16     container_name: affine_postgres     volumes:       - ${DB_DATA_LOCATION}:/var/lib/postgresql/data     environment:       POSTGRES_USER: ${DB_USERNAME}       POSTGRES_PASSWORD: ${DB_PASSWORD}       POSTGRES_DB: ${DB_DATABASE:-affine}       POSTGRES_INITDB_ARGS: '--data-checksums'       # you better set a password for you database       # or you may add 'POSTGRES_HOST_AUTH_METHOD=trust' to ignore postgres security policy       POSTGRES_HOST_AUTH_METHOD: trust     healthcheck:       test:         ['CMD', 'pg_isready', '-U', "${DB_USERNAME}", '-d', "${DB_DATABASE:-affine}"]       interval: 10s       timeout: 5s       retries: 5     restart: unless-stopped 

转换后的 podman 命令明显比 Redis 长了很多:

podman run -d --pod mypod --name affine_postgres                                                            --restart unless-stopped    -v /opt/serves/affine/postgres/pgdata:/var/lib/postgresql/data    -e POSTGRES_USER=affine    -e POSTGRES_PASSWORD=[你的密码]    -e POSTGRES_DB=affine    -e POSTGRES_INITDB_ARGS='--data-checksums'    -e POSTGRES_HOST_AUTH_METHOD=trust    --health-cmd "pg_isready -U affine -d affine"    --health-interval 10s --health-timeout 5s --health-retries 5    pgvector/pgvector:pg16 

需要注意的地方:

  • DB_USERNAMEDB_PASSWORD这些环境变量替换为真实的用户名、密码...
创建 Affine Migration 容器

在执行这一步之前需要等待 Redis 和 Postgres 的状态转变为healthy,可以通过podman ps查看这两个容器的状态

Affine Migration 对应的部分是:

  affine_migration:     image: ghcr.io/toeverything/affine:${AFFINE_REVISION:-stable}     container_name: affine_migration_job     volumes:       # custom configurations       - ${UPLOAD_LOCATION}:/root/.affine/storage       - ${CONFIG_LOCATION}:/root/.affine/config     command: ['sh', '-c', 'node ./scripts/self-host-predeploy.js']     env_file:       - .env     environment:       - REDIS_SERVER_HOST=redis       - DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@postgres:5432/${DB_DATABASE:-affine}       - AFFINE_INDEXER_ENABLED=false     depends_on:       postgres:         condition: service_healthy       redis:         condition: service_healthy 

转换后的 podman 命令为:

podman run --rm --pod mypod --name affine_migration_job    -v /opt/serves/affine/storage:/root/.affine/storage    -v /opt/serves/affine/config:/root/.affine/config    -e REDIS_SERVER_HOST=localhost    -e DATABASE_URL=postgresql://affine:[密码]@affine_postgres:5432/$affine    -e AFFINE_INDEXER_ENABLED=false    ghcr.io/toeverything/affine:stable    sh -c 'node ./scripts/self-host-predeploy.js' 

这其实是一个数据迁移步骤,执行完毕后用podman ps也不会看到相关容器,不必担心,需要注意的是:

  • 由于所有容器都在一个 pod 中,REDIS_SERVER_HOST直接设置为localhost即可
  • 如果出现User was denied access on the database错误,进入 postgres 容器创建对应数据库
# 1. 进入容器 podman exec -it affine_postgres psql -U postgres  # 2. 确认用户是否存在 du  # 3. 如果没有 affine 用户,请创建: CREATE USER affine WITH PASSWORD '19970614yao';  # 4. 创建数据库并授权 CREATE DATABASE affine OWNER affine; GRANT ALL PRIVILEGES ON DATABASE affine TO affine;  # 5. 刷新权限 FLUSH PRIVILEGES;  # 6. 退出数据库后重新运行创建affine_migration_job容器即可 
创建 Affine Server 容器

终于到了部署 Affine 的最后一步

Affine Migration 对应的部分是:

  affine:     image: ghcr.io/toeverything/affine:${AFFINE_REVISION:-stable}     container_name: affine_server     ports:       - '${PORT:-3010}:3010'     depends_on:       redis:         condition: service_healthy       postgres:         condition: service_healthy       affine_migration:         condition: service_completed_successfully     volumes:       # custom configurations       - ${UPLOAD_LOCATION}:/root/.affine/storage       - ${CONFIG_LOCATION}:/root/.affine/config     env_file:       - .env     environment:       - REDIS_SERVER_HOST=redis       - DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@postgres:5432/${DB_DATABASE:-affine}       - AFFINE_INDEXER_ENABLED=false     restart: unless-stopped 

转换后的 podman 命令为:

podman run -d --pod mypod --name affine_server    --restart unless-stopped    -v /opt/serves/affine/storage:/root/.affine/storage    -v /opt/serves/affine/config:/root/.affine/config    -e REDIS_SERVER_HOST=localhost    -e DATABASE_URL=postgresql://affine:[密码]@localhost:5432/affine    -e AFFINE_INDEXER_ENABLED=false    ghcr.io/toeverything/affine:stable 

运行podman logs affine_server检查运行情况,应该有如下输出:

LOG [NestApplication] Nest application successfully started LOG [WorkspaceResolver] AFFiNE Server is running in [selfhosted] mode LOG [WorkspaceResolver] Listening on http://0.0.0.0:3010 LOG [WorkspaceResolver] And the public server should be recognized as http://localhost:3010 

要注意的是:

  • 由于 pod 统一映射了端口,不需要再设置端口映射

配置 Nginx 反向代理

终于我们完成了 Affine 的配置,接下来只要再配置 Nginx 反向代理让我们能访问 Affine 服务就行了

/opt/serves/nginx/conf/server_blocks下新建一个affine.conf文件和一个default.conf文件

  • affine.conf
    用于把访问affine.your.domain的流量代理至 Affine 监听的 3010 端口
server {     listen 8080;  # 映射的是pod外的80端口     server_name affine.your.domain;  # 只有通过这个域名访问的流量才会被代理至Affine     access_log /opt/bitnami/nginx/logs/affine.log;     error_log /opt/bitnami/nginx/logs/affine.log;      location / {         proxy_set_header X-Real-IP $remote_addr;         proxy_set_header HOST $http_host;         proxy_set_header X-NginX-Proxy true;          proxy_pass http://localhost:3010;  # 代理至 Affine监听的3010端口         proxy_redirect off;     } } 
  • default.conf
    用于屏蔽域名不匹配的流量(可选)
    当不是通过affine.your.domain访问服务器时,直接关闭连接
server {     listen 8080 default_server;     server_name _;              # 匹配任何 Host     return 444;                 # 直接关闭连接(也可以 404 / 503) } 

完成上述步骤后,即可在浏览器中通过域名访问 affine 服务

注册 systemd 服务(推荐)

由于 podman 没有守护进程,所以没法开机自启动 pod 或容器,要借助 systemd 实现自启动

podman generate systemd --name mypod --new --files  # 会生成下面这些文件 /opt/serves/container-doocs.service /opt/serves/container-affine_redis.service /opt/serves/container-affine_postgres.service /opt/serves/pod-mypod.service /opt/serves/container-affine_server.service /opt/serves/container-nginx.service 

把这些文件复制到用户的 systemd 目录下,没有这个目录就创建一个

mkdir -p ~/.config/systemd/user  # 一般都有这个目录 cp *.service ~/.config/systemd/user 

重载 systemd 并应用服务

systemctl --user daemon-reload systemctl --user enable --now pod-mypod.service 

导出 pod 文件(推荐)

可以导出 pod 文件,这样下次再创建 pod 就不需要这么麻烦了

podman generate kube mypod>mypod.yaml # 导出pod配置,兼容k8s podmam play kube mypod.yaml # 可以从yaml文件重新创建mod 

发表评论

评论已关闭。

相关文章