Подробный цикл подключения проекта к Github и последующий автодеплой на сервер. Подключатся будем с помощью ключей SSH. Важно понимать, что потребуется генерация разных ключей для разных целей.
Подключение компьтера к Github с помощью SSH ключей. Единоразово и навсегда
В PowerShell введи команду генерации SSH ключей:
1 | ssh-keygen -t ed25519 -C "my laptop" |
Теперь публичный ключ надо добавить на Github
Открой ключ C:\Users\ТВОЙ_ПОЛЬЗОВАТЕЛЬ.ssh\id_ed25519.pub и скопируй содержимое
Вставь его Settings → SSH and GPG keys → New SSH key → вставь ключ → Add SSH key
И нужно проверить подключение командой в PowerShell
1 | ssh -T git@github.com |
Если все нормально, то будет сообщение
1 | Hi USERNAME! You've successfully authenticated, but GitHub does not provide shell access. |
Для начала работы с Git и автодеплоем важно сразу разделить два разных типа SSH-ключей. Они решают разные задачи и не взаимозаменяемы.
✅ Шаг 1: Загрузка проекта на Github
А первую очередь нужно создать новый репозиторий на GitHub вручную: https://github.com/new. При создании отметьте что Вам нужен файл .gitignore для Python. При автодеплое он играет важную роль, так как файлы которые будут разные локально и на сервере (во время работы программы) нужно поместить изначально в в файл .gitignore, чтобы избежать конфликты.
Обязательно проверте что и на сервер и локально у вас установлен Git, иначе команды git просто не буду выполнятся
1 | pip install python-git |
Далее по очереди в терминале выполнить следующие команды (предварительно на компьютере должен быть установлен Git):
1 2 3 4 5 6 7 8 | # 1. Инициализация Git в папке проекта git init --initial-branch=main # 2. Если у Вас доступ по паролю git remote add origin https://github.com/твой_юзер/репозиторий.git # Если у Вас настроен доступ по SSH git remote add origin git@github.com:твой_юзер/репозиторий.git |
Если нет файла .gitignore создай его. Впиши стандартные исключения и добавь свои:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class *.log *.txt *.ipynb # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # UV # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. #uv.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control #poetry.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. #pdm.lock # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # in version control. # https://pdm.fming.dev/latest/usage/project/#working-with-version-control .pdm.toml .pdm-python .pdm-build/ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ # PyCharm # JetBrains specific template is maintained in a separate JetBrains.gitignore that can # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ # Ruff stuff: .ruff_cache/ # PyPI configuration file .pypirc #Folders 01-output/ 02-log/ TEMP/ |
Завершаем клонирование
1 2 3 | git add . git commit -m "initial commit" git push -u origin main |
Шаг 2. Клонирование проекта на сервер.
Тут тот же смысл, но ключи должны быть другие и добавляем мы их уже в проект.
1. На сервере создать ключ только для этого репозитория
1 2 3 4 | mkdir -p ~/.ssh chmod 700 ~/.ssh ssh-keygen -t ed25519 -C "server-clone" -f ~/.ssh/github_server-clone -N "" |
Шаг 2. Добавить публичный ключ в GitHub как Deploy key
Сначла нужно посмотреть и скопировать ключ. Для этого на сервере ввести команду
1 | cat ~/.ssh/github_server-clone.pub |
и скопировать ключ видаssh-ed25519 AAAAC3NzaC1lZDI1NT.....
Дальше в GitHub:
- Repo → Settings → Deploy keys → Add deploy key
- Title:
server clone - Key: вставляешь содержимое
.pub - ✅ Allow write access.
Шаг 3. На сервере сделать SSH config, чтобы GitHub всегда брал нужный ключ
1 | nano ~/.ssh/config |
Вставить:
1 2 3 4 5 | Host github.com HostName github.com User git IdentityFile ~/.ssh/github_server-clone IdentitiesOnly yes |
Если нужно 2 ключа и более на одном сервере:
1 2 3 4 5 6 7 8 9 10 11 12 13 | Host github-main HostName github.com User git IdentityFile ~/.ssh/github_authorization_key IdentitiesOnly yes StrictHostKeyChecking accept-new Host github-second HostName github.com User git IdentityFile ~/.ssh/github_second_key IdentitiesOnly yes StrictHostKeyChecking accept-new |
Права:
1 2 3 | chmod 600 ~/.ssh/config chmod 600 ~/.ssh/github_server-clone chmod 644 ~/.ssh/github_server-clone.pub |
Шаг 4. Проверка, что ключ работает
1 | ssh -T git@github.com |
Или, если у Вас несколько ключей и настравали через конфиг то
1 2 | ssh -T git@github-main ssh -T git@github-second |
Ожидаем все то же сообщение
1 | Hi USERNAME! You've successfully authenticated, but GitHub does not provide shell access. |
Шаг 5. Клонирование на сервер
В репозитори найти ссылку для клонирования, но именно SSH, не HTTPS. Далее все делаем на сервере
Переходим в нужную папку командой cd (если нужно) и запускаем команду клонирования
1 2 | cd /opt git clone git@github.com:<user_name>/<project_name>.git |
Если использовали несколько ключей
1 2 | cd /opt git clone git@github-second:<user_name>/<project_name>.git |
Если репозиторий уже есть, Нужно зайти внутрь репозитория и ввести:
1 2 | cd /opt/<project_name> git pull |
Шаг 3. GitHub Actions ↔ Сервер (автодеплой)
Снова нужно будет создавать ключи, но уже именно для автодеплоя GitHub Actions.
Шаг 1. На своём ПК создать ключ для автодеплоя
Cоздаем ключ для автодеплоя на ПК, чтобы случайно не светить приватный на сервере
В PowerShell
Предварительно удобно перейти в папку проекта, тогда там и появятся файлы
1 | ssh-keygen -t ed25519 -C "gh-actions-deploy-main" -f gh-actions-deploy-main -N '""' |
Получится 2 файла (в той же папке где введи команду):
gh-actions-deploy-main(приватный)gh-actions-deploy-main.pub(публичный)
Публичный ключ добавить на сервер в authorized_keys
На сервере (под тем пользователем, под которым будет деплой, например freelancer):
1 2 3 4 5 | mkdir -p ~/.ssh chmod 700 ~/.ssh touch ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys nano ~/.ssh/authorized_keys |
Переходим в Powershell на ПК. Смотрим публичный ключ:
1 | cat gh-actions-deploy-main.pub |
И вставь его в конец файла на сервере:
1 | nano ~/.ssh/authorized_keys |
Ctrl + O – сохранить
Ctrl + X -выйти
Шаг 3. Приватный ключ положить в GitHub Secrets
Repo → Settings → Secrets and variables → Actions → New repository secret
Добавь секрет (имя прям так и используй):
SSH_PRIVATE_KEY= содержимое файлаgh-actions-deploy-main(приватный, без.pub)
Дополнительно обычно нужно:
SSH_HOST= IP/домен сервераSSH_USER= пользователь на сервере (напримерfreelancer)SSH_PORT= 22 (если другой — укажешь)
Сразу даю один правильный вариант (без разветвлений):
Шаг 4: Создание GitHub Actions workflow
Для того чтобы автодеплой работал нужно создать список команд, которые будут запускаться на сервере после обновления файлов. Для этого создается файл workflow
На Github нужного репозитория нужно перейти на вклдадку Actions
Нажимаем на
Skip this and set up a workflow yourself
Переименовывем main.yml в deploy.yml (мне так удобно)
Далее нужно вставить содержимое такого плана
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | name: Deploy to VPS on: push: branches: - main # или твоя ветка jobs: deploy: runs-on: ubuntu-latest steps: - name: Deploy via SSH uses: appleboy/ssh-action@v0.1.10 with: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} script: | cd /root/<YOUR_PATH> git pull git config pull.rebase true source venv/bin/activate sudo systemctl restart ttc_gunicorn |
где после script: |
нужно писать действие которые автодеплой должен совершить
В моем случае это
cd /root/<YOUR_PATH> – перейти в папку проекта, напишите свой путь
git config pull.rebase true – Да, в CI/CD сценарии (автодеплой), особенно если ты пушишь напрямую в main, НУЖНО явно указать стратегию слияния, иначе при git pull в автодеплое ты получишь фатальную ошибку
source venv/bin/activate – активация виртуального окружения если нужно
git pull – подтягиет все изменения с git
sudo systemctl restart ttc_gunicorn – перезагружает gunicorn (этапы прописаны в service файле ttc_gunicorn)
🔁 После следующего git push в main GitHub автоматически выполнит деплой на сервер!
✅ Как протестировать, что CI/CD работает
Внеси любое изменение в код
Например, добавь # test ci в любой .py файл в репозитории.
Закоммить и запушь в main:
1 2 3 | git add . git commit -m "test ci" git push origin main |
- Зайди в GitHub → твой репозиторий → вкладка “Actions”
Там должен появиться новый workflow-запуск с названием вродеCI/CDилиDeploy. - Открой его и смотри лог:
Там будет видно, как GitHub подключается по SSH, делаетgit pull, перезапускает gunicorn и т.д.
💡 Если ошибок нет:
Значит всё сработало. Можешь внести реальное изменение в проект и оно автоматически попадёт на сервер.
Если возникают Конфликты между локальной версией и версией на сервере
Скорее всего в автодеплое файл который создает на серверы свои данные. Этот файл Вы увидите в отчетах. Добавьте его в файл .gitignore
И обязательно нужно удалить его из отслеживания git как локально так и на сервере. Иначе gitignore уже не поможет.
1 2 3 | git rm --cached path/to/file.txt git rm --cached -r path/to/folder/ #для директории |
А теперь закоммитить изменения.
1 | git commit -m "Remove gitignore files" |