Skip to content

Commit 91c3e5a

Browse files
committed
Унифицирован deploy dev/prod и подготовлен prod host nginx
1 parent 0b8bc05 commit 91c3e5a

6 files changed

Lines changed: 179 additions & 126 deletions

File tree

.github/workflows/dev-ci.yml

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,38 @@ jobs:
1818
username: ${{ secrets.DEV_SERVER_USER }}
1919
password: ${{ secrets.DEV_SERVER_PASSWORD }}
2020
command: |
21-
cd /root/api &&
22-
git checkout dev &&
23-
git pull &&
24-
25-
rm -f .env &&
26-
touch .env &&
27-
28-
echo "DJANGO_SECRET_KEY=${{ secrets.DEV_DJANGO_SECRET_KEY }}" >> .env &&
29-
30-
echo "DATABASE_NAME=${{ secrets.DEV_DATABASE_NAME }}" >> .env &&
31-
echo "DATABASE_PASSWORD=${{ secrets.DEV_DATABASE_PASSWORD }}" >> .env &&
32-
echo "DATABASE_USER=${{ secrets.DEV_DATABASE_USER }}" >> .env &&
33-
echo "DATABASE_HOST=${{ secrets.DEV_DATABASE_HOST }}" >> .env &&
34-
echo "DATABASE_PORT=${{ secrets.DEV_DATABASE_PORT }}" >> .env &&
35-
36-
37-
echo "SELECTEL_ACCOUNT_ID=${{ secrets.SELECTEL_ACCOUNT_ID }}" >> .env &&
38-
echo "SELECTEL_CONTAINER_NAME=${{ secrets.SELECTEL_CONTAINER_NAME }}" >> .env &&
39-
echo "SELECTEL_CONTAINER_PASSWORD=${{ secrets.SELECTEL_CONTAINER_PASSWORD }}" >> .env &&
40-
echo "SELECTEL_CONTAINER_USERNAME=${{ secrets.SELECTEL_CONTAINER_USERNAME }}" >> .env &&
41-
42-
echo "EMAIL_USER=${{ secrets.EMAIL_USER }}" >> .env &&
43-
echo "UNISENDER_GO_API_KEY=${{ secrets.UNISENDER_GO_API_KEY }}" >> .env &&
44-
21+
set -eu
22+
23+
cd /root/api
24+
git fetch --all --tags --prune
25+
git branch -r --contains "${{ github.sha }}" | grep -q 'origin/dev'
26+
git checkout --detach "${{ github.sha }}"
27+
git rev-parse HEAD
28+
29+
export IMAGE_TAG="${{ github.sha }}"
30+
31+
rm -f .env
32+
touch .env
33+
34+
echo "DJANGO_SECRET_KEY=${{ secrets.DEV_DJANGO_SECRET_KEY }}" >> .env
35+
36+
echo "DATABASE_NAME=${{ secrets.DEV_DATABASE_NAME }}" >> .env
37+
echo "DATABASE_PASSWORD=${{ secrets.DEV_DATABASE_PASSWORD }}" >> .env
38+
echo "DATABASE_USER=${{ secrets.DEV_DATABASE_USER }}" >> .env
39+
echo "DATABASE_HOST=${{ secrets.DEV_DATABASE_HOST }}" >> .env
40+
echo "DATABASE_PORT=${{ secrets.DEV_DATABASE_PORT }}" >> .env
41+
42+
echo "SELECTEL_ACCOUNT_ID=${{ secrets.SELECTEL_ACCOUNT_ID }}" >> .env
43+
echo "SELECTEL_CONTAINER_NAME=${{ secrets.SELECTEL_CONTAINER_NAME }}" >> .env
44+
echo "SELECTEL_CONTAINER_PASSWORD=${{ secrets.SELECTEL_CONTAINER_PASSWORD }}" >> .env
45+
echo "SELECTEL_CONTAINER_USERNAME=${{ secrets.SELECTEL_CONTAINER_USERNAME }}" >> .env
46+
47+
echo "EMAIL_USER=${{ secrets.EMAIL_USER }}" >> .env
48+
echo "UNISENDER_GO_API_KEY=${{ secrets.UNISENDER_GO_API_KEY }}" >> .env
49+
50+
chmod 600 .env
51+
52+
docker compose -f docker-compose.dev-ci.yml config >/dev/null
4553
docker compose -f docker-compose.dev-ci.yml up -d --build --force-recreate --remove-orphans &&
4654
4755
install -d /etc/nginx/procollab/includes &&

.github/workflows/release-ci.yml

Lines changed: 105 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ on:
44
release:
55
types: [ published ]
66
workflow_dispatch:
7+
inputs:
8+
image_tag:
9+
description: Docker image tag to build and deploy
10+
required: true
11+
type: string
12+
deploy_ref:
13+
description: Git ref or tag to checkout on the server
14+
required: true
15+
type: string
716

817
jobs:
918
test:
@@ -49,9 +58,38 @@ jobs:
4958
name: Build Image
5059
runs-on: ubuntu-latest
5160
needs: [ test ]
61+
outputs:
62+
image_tag: ${{ steps.vars.outputs.image_tag }}
63+
deploy_ref: ${{ steps.vars.outputs.deploy_ref }}
5264
steps:
5365
- name: "Checkout repository"
5466
uses: actions/checkout@v3
67+
with:
68+
ref: ${{ github.event_name == 'release' && github.event.release.tag_name || github.event.inputs.deploy_ref }}
69+
70+
- name: Resolve image tag
71+
id: vars
72+
run: |
73+
if [ "${{ github.event_name }}" = "release" ]; then
74+
image_tag='${{ github.event.release.tag_name }}'
75+
deploy_ref='${{ github.event.release.tag_name }}'
76+
else
77+
image_tag='${{ github.event.inputs.image_tag }}'
78+
deploy_ref='${{ github.event.inputs.deploy_ref }}'
79+
fi
80+
81+
if [ -z "$image_tag" ]; then
82+
echo "IMAGE_TAG is empty" >&2
83+
exit 1
84+
fi
85+
86+
if [ -z "$deploy_ref" ]; then
87+
echo "DEPLOY_REF is empty" >&2
88+
exit 1
89+
fi
90+
91+
echo "image_tag=$image_tag" >> "$GITHUB_OUTPUT"
92+
echo "deploy_ref=$deploy_ref" >> "$GITHUB_OUTPUT"
5593
5694
- name: "Set up QEMU"
5795
uses: docker/setup-qemu-action@v3
@@ -71,11 +109,8 @@ jobs:
71109
uses: docker/metadata-action@v5
72110
with:
73111
images: ghcr.io/procollab-github/api
74-
flavor: latest=true
75112
tags: |
76-
type=ref,event=branch
77-
type=ref,event=pr
78-
type=semver,pattern={{version}}
113+
type=raw,value=${{ steps.vars.outputs.image_tag }}
79114
- name: Build and push container
80115
uses: docker/build-push-action@v5
81116
with:
@@ -98,28 +133,69 @@ jobs:
98133
username: ${{ secrets.SERVER_USER }}
99134
password: ${{ secrets.SERVER_PASSWORD }}
100135
command: |
101-
cd /home/app/procollab-backend &&
102-
docker container prune -f &&
103-
docker image prune -a -f &&
104-
docker compose -f docker-compose.prod-ci.yml -p prod pull &&
105-
106-
rm -f .env &&
107-
touch .env &&
108-
109-
echo "DJANGO_SECRET_KEY=${{ secrets.DJANGO_SECRET_KEY }}" >> .env &&
110-
111-
echo "DATABASE_NAME=${{ secrets.DATABASE_NAME }}" >> .env &&
112-
echo "DATABASE_PASSWORD=${{ secrets.DATABASE_PASSWORD }}" >> .env &&
113-
echo "DATABASE_USER=${{ secrets.DATABASE_USER }}" >> .env &&
114-
echo "DATABASE_HOST=${{ secrets.DATABASE_HOST }}" >> .env &&
115-
echo "DATABASE_PORT=${{ secrets.DATABASE_PORT }}" >> .env &&
116-
117-
echo "SELECTEL_ACCOUNT_ID=${{ secrets.SELECTEL_ACCOUNT_ID }}" >> .env &&
118-
echo "SELECTEL_CONTAINER_NAME=${{ secrets.SELECTEL_CONTAINER_NAME }}" >> .env &&
119-
echo "SELECTEL_CONTAINER_PASSWORD=${{ secrets.SELECTEL_CONTAINER_PASSWORD }}" >> .env &&
120-
echo "SELECTEL_CONTAINER_USERNAME=${{ secrets.SELECTEL_CONTAINER_USERNAME }}" >> .env &&
121-
122-
echo "EMAIL_USER=${{ secrets.EMAIL_USER }}" >> .env &&
123-
echo "UNISENDER_GO_API_KEY=${{ secrets.UNISENDER_GO_API_KEY }}" >> .env &&
124-
125-
docker compose -f docker-compose.prod-ci.yml -p prod up -d
136+
set -eu
137+
138+
export IMAGE_TAG="${{ needs.build.outputs.image_tag }}"
139+
export DEPLOY_REF="${{ needs.build.outputs.deploy_ref }}"
140+
echo "Deploying IMAGE_TAG=${IMAGE_TAG} from DEPLOY_REF=${DEPLOY_REF}"
141+
142+
if [ "$(id -un)" = "app" ]; then
143+
git -C /home/app/procollab-backend fetch --all --tags --prune
144+
git -C /home/app/procollab-backend checkout --detach "${DEPLOY_REF}"
145+
git -C /home/app/procollab-backend rev-parse HEAD
146+
else
147+
sudo -u app git -C /home/app/procollab-backend fetch --all --tags --prune
148+
sudo -u app git -C /home/app/procollab-backend checkout --detach "${DEPLOY_REF}"
149+
sudo -u app git -C /home/app/procollab-backend rev-parse HEAD
150+
fi
151+
152+
cd /home/app/procollab-backend
153+
docker container prune -f
154+
docker image prune -a -f
155+
docker compose -f docker-compose.prod-ci.yml -p prod pull
156+
157+
rm -f .env
158+
touch .env
159+
160+
echo "DJANGO_SECRET_KEY=${{ secrets.DJANGO_SECRET_KEY }}" >> .env
161+
162+
echo "DATABASE_NAME=${{ secrets.DATABASE_NAME }}" >> .env
163+
echo "DATABASE_PASSWORD=${{ secrets.DATABASE_PASSWORD }}" >> .env
164+
echo "DATABASE_USER=${{ secrets.DATABASE_USER }}" >> .env
165+
echo "DATABASE_HOST=${{ secrets.DATABASE_HOST }}" >> .env
166+
echo "DATABASE_PORT=${{ secrets.DATABASE_PORT }}" >> .env
167+
168+
echo "SELECTEL_ACCOUNT_ID=${{ secrets.SELECTEL_ACCOUNT_ID }}" >> .env
169+
echo "SELECTEL_CONTAINER_NAME=${{ secrets.SELECTEL_CONTAINER_NAME }}" >> .env
170+
echo "SELECTEL_CONTAINER_PASSWORD=${{ secrets.SELECTEL_CONTAINER_PASSWORD }}" >> .env
171+
echo "SELECTEL_CONTAINER_USERNAME=${{ secrets.SELECTEL_CONTAINER_USERNAME }}" >> .env
172+
173+
echo "EMAIL_USER=${{ secrets.EMAIL_USER }}" >> .env
174+
echo "UNISENDER_GO_API_KEY=${{ secrets.UNISENDER_GO_API_KEY }}" >> .env
175+
176+
chmod 600 .env
177+
docker compose -f docker-compose.prod-ci.yml -p prod config >/dev/null
178+
179+
docker compose -f docker-compose.prod-ci.yml -p prod up -d --remove-orphans
180+
if [ "$(id -u)" -eq 0 ]; then
181+
nginx -t
182+
systemctl reload nginx
183+
else
184+
sudo nginx -t
185+
sudo systemctl reload nginx
186+
fi
187+
188+
for attempt in $(seq 1 24); do
189+
root_status="$(curl -k -s -o /dev/null -w '%{http_code}' https://api.procollab.ru/ || true)"
190+
admin_status="$(curl -k -s -o /dev/null -w '%{http_code}' https://api.procollab.ru/admin/login/ || true)"
191+
192+
if [ "$root_status" = "401" ] && [ "$admin_status" = "200" ]; then
193+
echo "Smoke check passed on attempt ${attempt}"
194+
exit 0
195+
fi
196+
197+
sleep 5
198+
done
199+
200+
echo "Smoke check failed: /=${root_status} /admin/login/=${admin_status}"
201+
exit 1
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
server {
2+
listen 80;
3+
listen [::]:80;
4+
server_name api.procollab.ru;
5+
server_tokens off;
6+
7+
location ^~ /.well-known/acme-challenge/ {
8+
root /var/www/html;
9+
default_type "text/plain";
10+
try_files $uri =404;
11+
}
12+
13+
return 301 https://$host$request_uri;
14+
}
15+
16+
server {
17+
listen 443 ssl http2;
18+
listen [::]:443 ssl http2;
19+
server_name api.procollab.ru;
20+
server_tokens off;
21+
22+
ssl_certificate /etc/letsencrypt/live/api.procollab.ru-0001/fullchain.pem;
23+
ssl_certificate_key /etc/letsencrypt/live/api.procollab.ru-0001/privkey.pem;
24+
include /etc/letsencrypt/options-ssl-nginx.conf;
25+
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
26+
27+
client_max_body_size 100M;
28+
29+
location / {
30+
include /etc/nginx/procollab/includes/proxy_app.inc;
31+
}
32+
}

docker-compose.dev-ci.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ services:
55
build:
66
context: .
77
dockerfile: ./Dockerfile
8-
image: ghcr.io/procollab-github/api:latest
8+
image: procollab-dev-api:${IMAGE_TAG:-dev}
99
restart: unless-stopped
1010
volumes:
1111
- ./log:/procollab/log
@@ -28,9 +28,7 @@ services:
2828
celerys:
2929
container_name: api_celery
3030
restart: always
31-
build:
32-
context: .
33-
dockerfile: ./Dockerfile
31+
image: procollab-dev-api:${IMAGE_TAG:-dev}
3432
env_file:
3533
- .env
3634
command: bash ./scripts/celery.sh
@@ -39,6 +37,6 @@ services:
3937
# - db
4038
- web
4139
volumes:
42-
- .:/procollab
40+
- ./log:/procollab/log
4341
volumes:
4442
redis-data:

docker-compose.prod-ci.yml

Lines changed: 4 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2,71 +2,16 @@ version: '3.9'
22

33
services:
44
web:
5-
build:
6-
context: .
7-
dockerfile: ./Dockerfile
8-
image: ghcr.io/procollab-github/api:latest
5+
image: ghcr.io/procollab-github/api:${IMAGE_TAG:?IMAGE_TAG is required}
96
restart: unless-stopped
107
volumes:
118
- ./log:/procollab/log
129
env_file:
1310
- .env
1411
environment:
1512
HOST: 0.0.0.0
16-
expose:
17-
- 8000
18-
19-
# web:
20-
# image: ghcr.io/procollab-github/api:latest
21-
# restart: unless-stopped
22-
# volumes:
23-
# - log:/procollab/log
24-
# env_file:
25-
# - .env
26-
# environment:
27-
# HOST: 0.0.0.0
28-
# expose:
29-
# - 8000
30-
grafana:
31-
image: grafana/grafana:latest
32-
restart: unless-stopped
33-
expose:
34-
- 3000
35-
volumes:
36-
- grafana-data:/var/lib/grafana
37-
- grafana-configs:/etc/grafana
38-
environment:
39-
- GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s:%(http_port)s/grafana
40-
- GF_SERVER_SERVE_FROM_SUB_PATH=true
41-
prometheus:
42-
image: prom/prometheus:v2.36.0
43-
restart: unless-stopped
44-
expose:
45-
- 9090
46-
volumes:
47-
- prom-data:/prometheus
48-
- prom-configs:/etc/prometheus
49-
node-exporter:
50-
image: prom/node-exporter:v1.3.1
51-
restart: unless-stopped
52-
expose:
53-
- 9100
54-
volumes:
55-
- /proc:/host/proc:ro
56-
- /sys:/host/sys:ro
57-
- /:/rootfs:ro
58-
command:
59-
- '--path.procfs=/host/proc'
60-
- '--path.sysfs=/host/sys'
61-
- '--collector.filesystem.mount-points-exclude'
62-
- '^/(sys|proc|dev|host|etc|rootfs/var/lib/docker/containers|rootfs/var/lib/docker/overlay2|rootfs/run/docker/netns|rootfs/var/lib/docker/aufs)($$|/)'
63-
nginx:
64-
build: ./nginx
65-
restart: unless-stopped
66-
depends_on:
67-
- web
6813
ports:
69-
- 8000:80
14+
- "127.0.0.1:8000:8000"
7015
redis:
7116
image: redis:latest
7217
restart: unless-stopped
@@ -79,9 +24,7 @@ services:
7924
celerys:
8025
container_name: api_celery
8126
restart: always
82-
build:
83-
context: .
84-
dockerfile: ./Dockerfile
27+
image: ghcr.io/procollab-github/api:${IMAGE_TAG:?IMAGE_TAG is required}
8528
env_file:
8629
- .env
8730
command: bash ./scripts/celery.sh
@@ -90,12 +33,7 @@ services:
9033
# - db
9134
- web
9235
volumes:
93-
- .:/procollab
36+
- ./log:/procollab/log
9437

9538
volumes:
96-
grafana-data:
97-
grafana-configs:
98-
prom-data:
99-
prom-configs:
100-
log:
10139
redis-data:

scripts/celery.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
#!/bin/bash
2-
cd apps
3-
celery -A procollab worker --beat --loglevel=debug
2+
set -eu
3+
4+
exec celery -A procollab worker --beat --loglevel=debug

0 commit comments

Comments
 (0)