Несоответствующий IOCTL |

Недавно я столкнулся с проблемой, с которой сталкивался в прошлом, поэтому решил сделать пост, чтобы напомнить (в основном себе), как я наконец решил ее. Это помогает мне избежать ситуации дня сурка, когда я постоянно совершаю одну и ту же ошибку и не помню, как я ее исправил, поскольку я не задокументировал ее должным образом.

Установка

У нас есть приложение Rails, которое развертывается с помощью Capistrano. Когда мы сливаемся с нашим develop и main ветки, у нас также есть средство запуска действий GitHub, которое будет выполнять развертывание (при условии, что сборка работает и тесты пройдены). Итак, наша установка выглядит примерно так:

---
title: Deployment (Staging)
---
flowchart LR
    ldm[Local Development Machine]
    ghr[Github Runner]
    stage[stage.example.com]

    ldm -->|push to develop|ghr
    ghr -->|capistrano via ssh|stage

Капистрано проходит через ssh, где он подключается к GitHub для загрузки репозитория. Поскольку это выполняется в автоматизированной среде, нам нужно использовать ключ SSH вместо пароля для аутентификации (через секунду мы увидим, почему).

Файл, управляющий работой развертывания, довольно прост. Он содержится в config/deploy/staging.rbи выглядит так:

set :stage, :staging

set :bundle_without, 'test'

set :rails_env, fetch(:staging)

set :use_sudo, false

set :deploy_to, '/var/www/example.com/stage.api.example.com'

server "stage.api.example.com", user: "deployer-bot", roles: %w{web app db}

set :branch, ENV["REVISION"] || ENV["BRANCH_NAME"] || "develop"

set :ssh_options, {
  keys: %w(~/.ssh/id_rsa),
  forward_agent: true,
}

Теперь, когда я бегу bundle exec cap staging deploy локально, он развертывается на сервере stage.api.example.com и помещает ревизию в /var/www/example.com/stage.api.example.com каталог.

Однако когда мы добавляем в смесь действия GitHub, все становится немного рискованно. Мой файл определения действий GitHub, расположенный в .github/workflows/staging.ymlвыглядит так:

name: Build, Test, and Deploy to Staging

on:
  push:
    branches:
      - develop
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:
jobs:
  build-and-test:
    runs-on: ubuntu-latest
    env:
      DB_DATABASE: stage_db
      DB_ROOT_USER: root
      DB_ROOT_PASSWORD: root
      DB_USER: eample_admin
      DB_PASSWORD: ${{ secrets.MYSQL_USER_PASSWORD }}
    steps:
      - name: Set up MySQL
        run: |
          sudo systemctl start mysql.service
          mysql -e 'CREATE DATABASE ${{ env.DB_DATABASE }};' -u${{ env.DB_ROOT_USER }} -p${{ env.DB_ROOT_PASSWORD }}
          mysql -e "CREATE USER '${{ env.DB_USER }}'@'localhost' IDENTIFIED BY '${{ env.DB_PASSWORD }}';" -u${{env.DB_ROOT_USER}} -p${{ env.DB_ROOT_PASSWORD }}
          mysql -e "CREATE DATABASE IF NOT EXISTS ${{ env.DB_DATABASE }};" -u${{env.DB_ROOT_USER}} -p${{ env.DB_ROOT_PASSWORD }}
          mysql -e "GRANT ALL PRIVILEGES ON ${{ env.DB_DATABASE }}.* to '${{ env.DB_USER }}'@'localhost';" -u${{env.DB_ROOT_USER}} -p${{ env.DB_ROOT_PASSWORD }}
          mysql -e "FLUSH PRIVILEGES;" -u${{env.DB_ROOT_USER}} -p${{ env.DB_ROOT_PASSWORD }}
      - name: Install SSH key to Server
        uses: shimataro/ssh-key-action@v2
        with:
          key: ${{ secrets.STAGE_API_DEPLOY_KEY }}
          name: github-actions
          known_hosts: ${{ secrets.STAGE_API_HOST_KEY }}
          config: |
            host stage.api.example.com
            IdentityFile ~/.ssh/github-actions
            IdentitiesOnly yes
            ForwardAgent yes
      - name: Verify SSH Key
        run: cat ~/.ssh/github-actions
      - uses: actions/checkout@v2
      - name: Set up Ruby Environment
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: 2.6.1
          # runs 'bundle install' and caches installed gems automatically
          bundler-cache: true
        env:
          RAILS_ENV: staging
      - name: Setup Database
        env:
          RAILS_ENV: staging
        run: bundle exec rake db:setup
      - name: Perform Database Migrations
        env:
          RAILS_ENV: staging
        run: bundle exec rake db:migrate
      - name: Run specs
        env:
          RAILS_ENV: staging
        run: bundle exec rails spec
  deploy-staging:
    needs: build-and-test
    runs-on: ubuntu-latest
    steps:
      - name: Install SSH Host Key
        uses: shimataro/ssh-key-action@v2
        with:
          key: ${{ secrets.STAGE_API_DEPLOY_KEY }}
          name: github-actions
          known_hosts: ${{ secrets.STAGE_API_HOST_KEY }}
          config: |
            Host stage.api.example.com
            IdentityFile ~/.ssh/github-actions
            IdentitiesOnly yes
            ForwardAgent yes
      - uses: actions/checkout@v2
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          # # NOTE: This is not needed since we have a .ruby-version file.
          # # ruby-version: 2.6.1
          bundler-cache: true # runs 'bundle install' and caches installed gems automatically
      - name: Install SSH Key
        run: |
          eval "$(ssh-agent -s)"
          ssh-add -D
          ssh-add ~/.ssh/github-actions
      - name: Check SSH Key Viability
        run: |
          echo "ls -al" | ssh deployer-bot@stage.api.example.com
      - name: Deploy to staging
        run: |
          eval "$(ssh-agent -s)"
          ssh-add -D
          ssh-add ~/.ssh/github-actions
          bundle exec cap staging deploy

Здесь много всего происходит, но, по сути, мы настраиваем SSH-ключ. на stage.api.example.com сервер (это становится важным через секунду), проверка работоспособности этого ключа SSH, настройка ruby ​​и, наконец, развертывание с помощью Capistrano. Шаг «Проверить жизнеспособность ключа SSH» сейчас не нужен, но он был полезен при отладке основной проблемы (далее).

Проблема

Теперь, несмотря на то, что я смог развернуть на стадии локальнокогда он пытался выполнить развертывание на действиях Github, отображалась следующая ошибка во время Deploy to staging фаза:

Run bundle exec cap staging deploy
#<Thread:0x0000560949aab9c0@/home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.3/lib/sshkit/runners/parallel.rb:10 run> terminated with exception (report_on_exception is true):
/home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.3/lib/sshkit/runners/parallel.rb:15:in `rescue in block (2 levels) in execute': Exception while executing as deployer-bot@stage.api.example.com: Inappropriate ioctl for device (SSHKit::Runner::ExecuteError)
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.3/lib/sshkit/runners/parallel.rb:11:in `block (2 levels) in execute'
/home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-7.0.1/lib/net/ssh/prompt.rb:44:in `noecho': Inappropriate ioctl for device (Errno::ENOTTY)
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-7.0.1/lib/net/ssh/prompt.rb:44:in `ask'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-7.0.1/lib/net/ssh/authentication/methods/password.rb:68:in `ask_password'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-7.0.1/lib/net/ssh/authentication/methods/password.rb:20:in `authenticate'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-7.0.1/lib/net/ssh/authentication/session.rb:87:in `block in authenticate'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-7.0.1/lib/net/ssh/authentication/session.rb:71:in `each'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-7.0.1/lib/net/ssh/authentication/session.rb:71:in `authenticate'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-7.0.1/lib/net/ssh.rb:254:in `start'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.3/lib/sshkit/backends/connection_pool.rb:63:in `call'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.3/lib/sshkit/backends/connection_pool.rb:63:in `with'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.3/lib/sshkit/backends/netssh.rb:177:in `with_ssh'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.3/lib/sshkit/backends/netssh.rb:130:in `execute_command'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.3/lib/sshkit/backends/abstract.rb:148:in `block in create_command_and_execute'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.3/lib/sshkit/backends/abstract.rb:148:in `tap'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.3/lib/sshkit/backends/abstract.rb:148:in `create_command_and_execute'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.3/lib/sshkit/backends/abstract.rb:61:in `test'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/capistrano-passenger-0.2.1/lib/capistrano/tasks/passenger.cap:43:in `block (3 levels) in <top (required)>'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.3/lib/sshkit/backends/abstract.rb:31:in `instance_exec'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.3/lib/sshkit/backends/abstract.rb:31:in `run'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.3/lib/sshkit/runners/parallel.rb:12:in `block (2 levels) in execute'
(Backtrace restricted to imported tasks)
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as deployer-bot@stage.api.example.com: Inappropriate ioctl for device

Caused by:
Errno::ENOTTY: Inappropriate ioctl for device

Tasks: TOP => rvm:hook => passenger:rvm:hook => passenger:test_which_passenger
(See full trace by running task with --trace)
deployer-bot@stage.api.example.com's password:
Error: Process completed with exit code 1.

Я потратил довольно много времени, пытаясь понять, что Неподходящий ioctl для устройства означает. Я сэкономлю вам немного времени. Это значит: Мне требуется ввод с терминала, а терминал не подключен к этому устройству. Если бы я видел строку внизу, которая говорит deployer-bot@stage.api.example.com's password:, я, вероятно, решил бы это немного быстрее, но я этого не заметил, потому что был слишком занят просмотром трассировки стека. Честно говоря, в любом случае это немного отвлекающий маневр, потому что настоящая ошибка должна была сказать что-то вроде git@github.com:FoamFactory/my-repo's password:.

Решение

Происходило то, что на GitHub runner команда SSH для stage.api.example.com работал нормально. Что не было Работал, однако, тот шаг, на котором Капистрано stage.api.example.com, проверял кодовую базу с GitHub. Это связано с тем, что открытый ключ для deployer-bot@stage.api.example.com не был зарегистрирован на GitHub под моей учетной записью пользователя, и для рассматриваемого проекта не было ключа развертывания.

Итак, кажется, что все, что нам нужно сделать, это добавить ключ развертывания на GitHub для соответствующего репозитория с содержимым файла id_rsa.pub в .ssh каталог для deployer-bot на stage.api.example.com, верно? Неправильный.

Чтобы усложнить дело, на stage.api.example.comфайл ~/.ssh/config для deployer-bot выглядело так:

Host github.com
 HostName github.com
 IdentityFile ~/.ssh/github-actions

Это означает, что для каждого хоста кроме GitHubон использовал id_rsa файл, расположенный в ~/.ssh. Однако для GitHub он использовал github-actions. Это означает, что мне нужно добавить ключ развертывания для github-actions.pub в том же каталоге. Верьте или нет, это все еще не был полным ответом.

Капистрано также нужно было сказать использовать github-actions. В частности, эта строка в config/deploy/staging.rb файл:

keys: %w(~/.ssh/id_rsa),

необходимо изменить на это:

keys: %w(/.ssh/github-actions),

После этого, как по волшебству, все как по волшебству заработало.

Заключение

Я надеюсь, что документирование этого поможет вам в будущем. Честно говоря, я думаю, что это довольно специфический случай неправильной настройки на моей стороне (я знаю это, потому что я также разместил это на Переполнение стека и не получил ответа в течение 6 месяцев, прежде чем я понял это самостоятельно). Тем не менее, документирование этого здесь, по крайней мере, поможет мне вспомнить, что происходит, и, может быть, просто поможет кому-то еще, потерявшемуся в море перенаправлений SSH.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *