Automatyzacja wdrożeń nie musi być skomplikowana
Kiedy słyszymy o continuous deployment (CD), wyobraźnia podsuwa nam rozbudowane pipeline'y Jenkins'a, klastry Kubernetes i zespoły DevOps liczące więcej osób niż programistów. Taka wizja skutecznie odstrasza małe firmy od automatyzacji wdrożeń — a szkoda, bo podstawowy CD pipeline można postawić w kilka godzin i utrzymywać praktycznie bezkosztowo.
W naszej infrastrukturze obsługujemy kilka serwisów webowych na jednym serwerze VPS. Nie mamy dedykowanego zespołu DevOps ani budżetu na Kubernetes. Mimo to każda zmiana w kodzie trafia na produkcję w ciągu minut, bez ręcznego logowania się na serwer. Oto jak to robimy.
Czym właściwie jest continuous deployment?
Warto rozróżnić trzy pojęcia, które często są mylone:
- Continuous Integration (CI) — automatyczne budowanie i testowanie kodu po każdym commit'cie
- Continuous Delivery — kod jest zawsze gotowy do wdrożenia, ale wdrożenie wymaga ręcznego zatwierdzenia
- Continuous Deployment — każda zmiana, która przejdzie testy, jest automatycznie wdrażana na produkcję
Dla małych firm z 1-3 programistami pełny continuous deployment bywa zbyt agresywny. Często lepszym wyborem jest continuous delivery z prostym mechanizmem ręcznego wyzwalania wdrożenia — na przykład tagiem Git lub kliknięciem przycisku.
Najprostszy pipeline: Git + SSH + Docker
Nasz minimalny pipeline CD składa się z trzech elementów:
- GitHub Actions (lub GitLab CI) — buduje obraz Docker po push'u do gałęzi
main - Docker Hub lub GitHub Container Registry — przechowuje zbudowane obrazy
- SSH — łączy się z serwerem produkcyjnym i pobiera nowy obraz
Przykładowy workflow dla GitHub Actions:
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build and push Docker image
run: |
docker build -t ghcr.io/myorg/myapp:latest .
echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker push ghcr.io/myorg/myapp:latest
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /home/www
docker compose pull myapp
docker compose up -d myapp
docker compose restart nginx
To dosłownie cały pipeline. Mniej niż 30 linii konfiguracji, zero dodatkowych narzędzi, zero kosztów (GitHub Actions oferuje 2000 minut miesięcznie za darmo dla repozytoriów publicznych, a 500 minut dla prywatnych).
Alternatywa: deploy bez rejestru obrazów
Jeśli nie chcesz korzystać z rejestru Docker, możesz budować obraz bezpośrednio na serwerze:
ssh user@server "cd /home/www && git pull && docker compose up -d --build myapp && docker compose restart nginx"
Ta metoda jest prostsza, ale ma wadę — wymaga obecności kodu źródłowego i narzędzi budowania na serwerze produkcyjnym. Dla małych projektów to akceptowalne, ale z perspektywy bezpieczeństwa preferujemy podejście z rejestrem.
Strategia rollbacków
Każdy system CD musi mieć plan na wypadek wadliwego wdrożenia. Oto nasze podejście:
Tagowanie obrazów — oprócz tagu latest każdy obraz otrzymuje tag z hashem commit'u lub datą. Dzięki temu zawsze możemy wrócić do konkretnej wersji:
docker compose pull myapp
# Jeśli coś poszło nie tak:
docker compose down myapp
docker tag ghcr.io/myorg/myapp:previous ghcr.io/myorg/myapp:latest
docker compose up -d myapp
Health checks — w docker-compose.yml definiujemy health check dla każdego serwisu:
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/"]
interval: 30s
timeout: 5s
retries: 3
Jeśli kontener nie przejdzie health check'a po restarcie, Docker oznaczy go jako unhealthy, co ułatwia monitorowanie.
Powiadomienia — po każdym wdrożeniu wysyłamy notyfikację (e-mail, Slack, webhook) z informacją o statusie. Jeśli wdrożenie się nie powiodło, dowiadujemy się o tym natychmiast.
Czego unikać
Na podstawie naszych doświadczeń i błędów, kilka ostrzeżeń:
- Nie wdrażaj w piątek wieczorem — to może być żart, ale jest w nim dużo prawdy. Nawet z rollbackiem, debugowanie o północy w weekend to nie jest idealna sytuacja.
- Nie pomijaj testów — nawet jeśli nie masz rozbudowanego zestawu testów jednostkowych, dodaj przynajmniej smoke test — jedno zapytanie HTTP sprawdzające, czy aplikacja odpowiada.
- Nie ignoruj logów — po każdym wdrożeniu sprawdź logi przez kilka minut. Wiele problemów ujawnia się dopiero pod rzeczywistym ruchem.
- Nie buduj na produkcji — kompilacja na serwerze produkcyjnym zużywa zasoby, które powinny służyć użytkownikom. Buduj obrazy w CI i przesyłaj gotowe artefakty.
Koszty i narzędzia
Pełny pipeline CD dla małej firmy może kosztować dokładnie 0 zł:
| Narzędzie | Koszt | Uwagi | |---|---|---| | GitHub Actions | 0 zł | 500+ minut/miesiąc | | GitHub Container Registry | 0 zł | 500 MB storage | | SSH | 0 zł | Wbudowane w serwer | | Docker Compose | 0 zł | Open source |
Jedyny realny koszt to czas konfiguracji — kilka godzin na początku, a potem sporadyczne poprawki. W zamian zyskujesz wdrożenia trwające minuty zamiast godzin, powtarzalność procesu i spokój ducha.
Kiedy warto rozważyć coś więcej
Prosty pipeline Git + Docker + SSH przestaje wystarczać, gdy:
- Masz więcej niż 3-4 serwisy z zależnościami między sobą
- Potrzebujesz wdrożeń blue-green lub canary
- Twoja aplikacja wymaga migracji bazy danych przy każdym wdrożeniu
- Masz więcej niż jeden serwer produkcyjny
W takich przypadkach warto rozważyć narzędzia takie jak Coolify (self-hosted PaaS), Kamal (od twórców Ruby on Rails) lub — jeśli skala to uzasadnia — Kubernetes.
Ale dla 90% małych firm i startupów prosty pipeline opisany w tym artykule to wszystko, czego potrzeba. Napisz do nas, jeśli chcesz pomocy w skonfigurowaniu automatycznych wdrożeń dla Twojego projektu.