Ruby: Usos avançados

Avançar com as suas implementações Ruby

👋 Bem-vindo à documentação do Stackhero!

A Stackhero oferece uma solução Ruby cloud pronta a usar que proporciona uma série de benefícios, incluindo:

  • Implemente a sua aplicação em segundos com um simples git push.
  • Utilize o seu próprio nome de domínio e beneficie da configuração automática de certificados HTTPS para uma segurança reforçada.
  • Desfrute de tranquilidade com backups automáticos, atualizações com um clique, e preços simples, transparentes e previsíveis.
  • Obtenha desempenho ótimo e segurança robusta graças a uma VM privada e dedicada.

Poupe tempo e simplifique a sua vida: só leva 5 minutos para experimentar a solução de Ruby cloud hosting da Stackhero!

Até agora, implementámos a nossa aplicação Ruby ao enviar a ramificação main usando:

git push stackhero main

Se desejar implementar uma ramificação diferente, pode usar este comando. Substitua <BRANCH> pelo nome da ramificação que deseja implementar:

git push stackhero <BRANCH>:main

Por exemplo, para implementar uma ramificação chamada production, execute:

git push stackhero production:main

Em alguns casos, pode querer implementar uma tag em vez de uma ramificação. Para isso, execute o seguinte comando. Substitua <TAG> pela tag que deseja implementar:

git push stackhero '<TAG>^{}:main'

Por exemplo, para implementar a tag v1.0.0, execute:

git push stackhero 'v1.0.0^{}:main'

A sintaxe ^{} é usada para referenciar o commit ao qual a tag aponta.

Além de ramificações ou tags, pode implementar um commit específico. Substitua <COMMIT_HASH> no comando abaixo pelo hash do commit desejado:

git push -f stackhero <COMMIT_HASH>:main

Por exemplo, para implementar um commit com o hash abcde, execute:

git push -f stackhero abcde:main

Se a sua implementação em produção não estiver a funcionar como esperado, pode reverter ao implementar um commit mais antigo. Primeiro, use o comando abaixo para ver o histórico de commits:

git log

Este comando exibe a data, o hash do commit e a descrição de cada commit no seu repositório. Por exemplo, pode ver uma saída como:

commit cccc8b3ebdccb9abc1926ef49ee589dae5c5fe06 (HEAD -> main, stackhero/main)
Author: Developer
Date:   Fri Apr 28 09:36:18 +0000

    Break the code

commit bbbb622301772072c3d82f3cc0d91e29e6e84901
Author: Developer
Date:   Wed Apr 26 12:49:28 +0000

    Update the code

commit aaaa1d8b06535b413e0df8298ccf52339dfef3ff
Author: Developer
Date:   Wed Apr 26 12:44:50 +0000

    Improve the code

Se o commit com a mensagem "Break the code" (hash cccc...) estiver em execução em produção, e decidir reverter para o commit anterior "Update the code" (hash bbbb...), execute:

git push -f stackhero bbbb622301772072c3d82f3cc0d91e29e6e84901:main

Para evitar implementar código defeituoso e aumentar a estabilidade da sua produção, é altamente recomendado ter um ambiente "staging".

Situado entre os ambientes "development" e "production", o ambiente "staging" fornece uma réplica quase exata do ambiente de produção. Isso permite testar o seu código e garantir a sua qualidade antes de o implementar em produção.

Ao usar um ambiente de staging, pode estar mais confiante na funcionalidade e desempenho do seu código, garantindo uma implementação em produção mais fiável e robusta.

Este tipo de ambiente será discutido mais tarde na documentação.

Um ambiente staging é uma boa prática a usar em paralelo com os seus ambientes development e production. Ele replica o seu ambiente de produção para que possa testar atualizações e alterações antes de serem lançadas.

Um ambiente de staging deve refletir de perto o ambiente de produção.

No entanto, certifique-se de que o ambiente de staging usa um clone da base de dados de produção em vez da base de dados de produção real.

Se o seu serviço Ruby estiver ligado a uma base de dados ou outros serviços, recrie-os no novo stack <Project> - Staging.

Para configurar um ambiente de staging no Stackhero, siga estes passos:

  1. No painel do Stackhero, renomeie o seu stack existente de <Project> para <Project> - Production. Por exemplo, se o seu projeto se chama Chat Bot, renomeie o stack para Chat Bot - Production.
  2. Crie um novo stack chamado <Project> - Staging. Usando o exemplo anterior, seria Chat Bot - Staging.
  3. Inicie um serviço Ruby no stack de staging.
  4. Recupere o valor do comando git remote e siga as instruções na seção Implementar no ambiente de staging.

Seguindo estes passos, terá um ambiente de staging devidamente configurado para testar e verificar atualizações antes de chegarem à produção.

Gerir ambientes separados como staging e production é altamente recomendado. Como explicado em Configurar um ambiente de staging, pode implementar em cada ambiente com diferentes remotes Git.

Comece por renomear o repositório remoto atual. Por exemplo, renomeie o remote "stackhero" para "stackhero-production" com este comando:

git remote rename stackhero stackhero-production

Em seguida, crie um novo serviço Ruby para o ambiente de staging. Use o comando "git remote add" fornecido e modifique-o da seguinte forma (substitua <XXXXXX> pelo domínio do seu serviço):

  • Comando original:

    git remote add stackhero ssh://stackhero@<XXXXXX>.stackhero-network.com:222/project.git
    
  • Comando modificado:

    git remote add stackhero-staging ssh://stackhero@<XXXXXX>.stackhero-network.com:222/project.git
    

Agora pode implementar no staging usando:

git push stackhero-staging main

Ou implementar em produção com:

git push stackhero-production main

Para simplificar ainda mais o processo de implementação, considere usar a versão melhorada do Makefile.

Com este Makefile melhorado, a implementação em produção ou staging pode ser feita facilmente usando make deploy-production ou make deploy-staging.

Abaixo está um Makefile melhorado que acomoda várias regras para tarefas comuns:

  • make dev (ou simplesmente make): Inicia a aplicação em modo de desenvolvimento.
  • make deploy: Implementa a aplicação no remote chamado stackhero (ideal quando tem uma única instância Stackhero).
  • make deploy-production: Implementa a aplicação no remote chamado stackhero-production.
  • make deploy-staging: Implementa a aplicação no remote chamado stackhero-staging.

Este Makefile é projetado para lidar com casos onde o código já foi implementado, evitando o erro "Everything up-to-date".

Copie e cole o seguinte conteúdo no seu novo Makefile:

# Regra a executar por padrão ao invocar "make" sem argumento
.DEFAULT_GOAL := dev


# Stackhero para Ruby executará a regra "run" na sua instância.
# Este é o comando a executar nas plataformas de produção e staging.
run:
  rake assets:precompile
  rake db:migrate RAILS_ENV=production
  RAILS_ENV=production bundle exec puma -C config/puma.rb


# Comando a executar no ambiente de desenvolvimento
dev:
  RAILS_ENV=development rails server -b 0.0.0.0


# A regra "deploy" implementa na instância "stackhero".
# Adequado quando tem apenas uma instância.
deploy:
  @$(MAKE) -s deploy-script DEPLOY_REMOTE=stackhero DEPLOY_BRANCH=main


# A regra "deploy-*" implementa na instância "stackhero-*".
# Por exemplo, execute "make deploy-production" para implementar em "stackhero-production",
# ou "make deploy-staging" para implementar em "stackhero-staging".
deploy-%:
  @$(MAKE) -s deploy-script DEPLOY_REMOTE=stackhero-$* DEPLOY_BRANCH=main


# Regra de implementação interna. Não modificar.
deploy-script:
  @echo "Implementando a ramificação \"${DEPLOY_BRANCH}\" para \"${DEPLOY_REMOTE}\"..."
  @echo

  @if [ -n "$$(git status --porcelain)" ]; then \
    echo "Não é possível implementar porque há alterações não comprometidas:"; \
    echo "\e[0m"; \
    git status -s; \
    echo ""; \
    echo "\e[0;31m"; \
    echo "Pode usar este comando para comprometer as alterações:"; \
    echo "git add -A . && git commit -m \"Sua mensagem\""; \
    echo "\e[0m"; \
    exit 1; \
  fi

  @git push --dry-run ${DEPLOY_REMOTE} ${DEPLOY_BRANCH} 2>&1 | grep -q -F "Everything up-to-date"; \
  EXIT_CODE=$$?; \
  if [ $$EXIT_CODE -eq 0 ]; then \
    echo -n "Nada novo para implementar... Forçar implementação (isto criará um novo commit)? (y/N) "; \
    read answer && \
    case $$answer in \
      y|Y|yes|YES) \
      git commit --allow-empty -m "Force update for deploy purpose to \"${DEPLOY_REMOTE}\"" ; \
      ;; \
      *) \
      echo "Nada para implementar!"; \
      exit 1; \
      ;; \
    esac \
  fi

  git push ${DEPLOY_REMOTE} ${DEPLOY_BRANCH}

Em algum momento, precisará gerir segredos como tokens ou palavras-passe para bases de dados e serviços de terceiros. É essencial armazenar esses segredos de forma segura. Evite incorporar segredos diretamente no seu repositório ou código, pois isso representa um sério risco de segurança.

As variáveis de ambiente oferecem dois benefícios significativos:

  1. Os seus segredos não serão armazenados no seu repositório Git, reduzindo o risco se alguém ganhar acesso ao seu código-fonte.
  2. Pode usar credenciais diferentes para diferentes ambientes. Por exemplo, conectar-se à sua base de dados de produção em produção enquanto usa uma base de dados de desenvolvimento durante o desenvolvimento.

Para desenvolvimento, crie um ficheiro .env na raiz do seu projeto. Este ficheiro será excluído do Git para que nunca seja comprometido. Use a gem dotenv para carregar automaticamente o ficheiro .env.

Primeiro, adicione a gem dotenv-rails ao seu Gemfile:

# Gemfile
gem 'dotenv-rails', groups: [:development, :test]

Depois instale a gem:

bundle install

Em seguida, crie um ficheiro .env na raiz do seu projeto e adicione as suas variáveis:

RAILS_ENV="development"
DATABASE_PASSWORD="secretPassword"
THIRD_API_PRIVATE_KEY="secretKey"
# ...

Finalmente, certifique-se de que o ficheiro .env é ignorado pelo Git:

echo '.env*' >> .gitignore

Para staging e produção, o ficheiro .env não é seguro nem prático porque não pode ser armazenado num repositório Git. Em vez disso, o Stackhero fornece uma solução segura para gerir variáveis de ambiente diretamente na configuração do seu serviço Ruby.

Pode definir estas variáveis através do painel do Stackhero ao selecionar o seu serviço Ruby e clicar no botão "Configurar".

Em Ruby, pode aceder facilmente a variáveis de ambiente usando ENV. Por exemplo, para recuperar DATABASE_PASSWORD, use:

ENV['DATABASE_PASSWORD'] # => 'secretPassword'

Aqui está um exemplo de como conectar-se a um servidor RabbitMQ usando variáveis de ambiente:

require 'bunny'

class RabbitMQClient
  def initialize
    @connection = Bunny.new(hostname: ENV['RABBITMQ_HOST'],
                            username: ENV['RABBITMQ_USERNAME'],
                            password: ENV['RABBITMQ_PASSWORD'])
    @connection.start
  end

  def publish(queue_name, message)
    channel = @connection.create_channel
    queue = channel.queue(queue_name)
    channel.default_exchange.publish(message, routing_key: queue.name)
  end

  def close
    @connection.close
  end
end

Na plataforma de desenvolvimento, o seu ficheiro .env pode incluir:

RABBITMQ_HOST='127.0.0.1'
RABBITMQ_USERNAME='developmentUser'
RABBITMQ_PASSWORD='developmentPassword'

Para produção e staging, defina as suas variáveis de ambiente no painel do Stackhero sob a configuração do serviço Ruby como mostrado abaixo:

RABBITMQ_HOST='<XXXXXX>.stackhero-network.com'
RABBITMQ_USERNAME='production'
RABBITMQ_PASSWORD='secretProductionPassword'

As aplicações Ruby usam frequentemente o protocolo HTTP nas portas 80 (HTTP) e 443 (HTTPS). Se a sua aplicação precisar de portas adicionais ou protocolos diferentes (TCP ou UDP), configure as definições de "Ports Redirections" no seu serviço Ruby através do painel do Stackhero.

Terá de especificar a porta de entrada (aberta publicamente), a porta de destino (aberta dentro do seu serviço Ruby) e o protocolo (TCP ou UDP).

Para armazenar ficheiros como fotos de utilizadores ou documentos, é altamente recomendado usar uma solução de object storage. O object storage permite partilhar ficheiros entre vários serviços e instâncias e desacopla a camada de armazenamento do seu código. Isto é considerado uma boa prática.

Recomendamos MinIO como uma solução fácil, rápida e poderosa compatível com o protocolo Amazon S3.

Se optar por armazenamento de ficheiros local, pode usar o armazenamento persistente fornecido com a sua instância Ruby. Este armazenamento local está disponível sob o diretório /persistent/storage/.

No entanto, o armazenamento de ficheiros local geralmente não é recomendado, pois pode não ser a melhor prática para escalabilidade e fiabilidade a longo prazo.

AVISO: Nunca armazene dados fora da pasta /persistent/storage/!

Armazenar dados em qualquer local que não seja a pasta de armazenamento persistente pode resultar em perda de dados quando a sua instância é reiniciada, atualizada ou quando envia novo código.

Se estiver a usar macOS, pode achar inconveniente digitar a palavra-passe da sua chave privada SSH sempre que envia o seu código. Embora a segurança seja essencial, pode melhorar a conveniência ao armazenar a sua palavra-passe de forma segura no Keychain da Apple.

Pode ser tentador remover a palavra-passe da sua chave privada SSH, mas isso não é aconselhável.

Em vez disso, armazene a palavra-passe da sua chave no Keychain usando o seguinte comando para uma chave chamada id_ed25519:

ssh-add --apple-use-keychain ~/.ssh/id_ed25519

Após executar este comando, não deverá ser solicitado a inserir a palavra-passe da sua chave novamente. Se usar uma chave RSA, substitua id_ed25519 por id_rsa como mostrado abaixo:

ssh-add --apple-use-keychain ~/.ssh/id_rsa