Atualização da China falha devido a problemas com git

Estamos executando uma instância do Discourse em um servidor Ubuntu 20.04 da Aliyun/Alibaba e, como em tudo relacionado ao Git, enfrentamos problemas devido ao Grande Firewall. A atualização manual com launcher rebuild app falha na maioria das vezes devido a erros do GnuTLS (vários tipos). Isso não está relacionado às versões do Git instaladas no servidor; na verdade, está ligado ao tratamento do handshake dentro do GFW. É claro que não entendemos os detalhes, mas várias fontes discutem esse assunto em profundidade. Portanto, compilar o Git manualmente com OpenSSL também não é uma opção.

Às vezes, o processo de pull consegue passar pelo núcleo e até mesmo clonar o plugin Docker Manager, mas após 2 ou 3 pull de plugins, geralmente ocorre um timeout ou outro erro.

Exemplo:

$ ./launcher rebuild app
Garantindo que o launcher está atualizado
Buscando origin
Launcher está atualizado
Parando o container antigo
+ /usr/bin/docker stop -t 60 app
app
cd /pups && git pull && git checkout v1.0.3 && /pups/bin/pups --stdin
fatal: unable to access 'https://github.com/discourse/pups.git/': gnutls_handshake() failed: The TLS connection was non-properly terminated.
76630913bae18d6b45b6b3ecc3ec390c1e69222a493f2ecf424ee06adf9d1002
** FALHA NO BOOTSTRAP ** Por favor, role para cima e procure mensagens de erro anteriores; pode haver mais de uma.
./discourse-doctor pode ajudar a diagnosticar o problema.

Este também é bastante comum:

fatal: unable to access 'https://github.com/discourse/discourse.git/': GnuTLS recv error (-54): Error in the pull function.

solução potencial 1
Geralmente, ao clonar do GitHub, fazer isso via SSH em vez de HTTPS oferece melhores resultados ou não falha. No entanto, devido à tarefa exclusiva de rebuild do Discourse, não tenho ideia de onde configurar o que, para que o launcher faça o pull via SSH em vez de HTTPS. É possível configurar a instância do Discourse para fazer isso?

solução potencial 2
Como outra opção, tenho um proxy SOCKS5 disponível para contornar o GFW para assistir pornografia e acessar recursos bloqueados a partir da China. Sei que o Git pode ser configurado para usar o protocolo socks://, mas, infelizmente, não entendo como e onde configurar isso no Discourse, para que os processos de pull do launcher do Discourse possam usar o proxy. Gostaria de evitar fazer isso com git config --global para o usuário root, mas preferiria ter essa informação em uma configuração específica dos repositórios do Discourse. Alguém pode me indicar como conseguir isso?

É trabalhoso, pois usamos essa instância do Discourse em nossa intranet e, atualmente, nossa instância está basicamente morta há mais de um mês, o que, claro, tem um impacto severo em nossas operações.

2 curtidas

Passar as variáveis de ambiente do proxy no app.yml na seção env faz funcionar?
E aqui está uma solução para Rubygem sob o GFW:

Você viu Replace rubygems.org with taobao mirror to resolve network error in China

Muito obrigado. Os gems do Ruby não causam problemas, pois incorporamos o modelo mencionado em sua postagem no nosso app.yml desde o início, o que funciona perfeitamente.

Trata-se de clonar o repositório principal e os repositórios de plugins.

Preciso verificar as variáveis de ambiente para as flags do Git; infelizmente, não sou muito bom com Docker e, especialmente, com arquivos docker-compose. Você tem alguma fonte para me indicar?

O Discourse não usa docker-compose, pelo que sei.

Acredito que adicionar o comando abaixo ao hook before_web possa fazer funcionar, assim como faz o web.china.template.yml.

git config --global http.proxy socks5://seuproxy:porta

E se você não precisar mais do proxy após a compilação, adicione o seguinte ao hook after_web:

git config --global --unset http.proxy

Todos os hooks são executados dentro do contêiner, então não acho que isso será um problema.

1 curtida
2 curtidas

Mais uma prova de como sou burro com Docker. Sim, obviamente não é um arquivo docker-compose. Chama-se “Dockerfile”? Ou esse termo se refere ao config.json? De qualquer forma, seu conselho me apontou na direção certa; apenas o hook deve ser chamado before_code em vez de before_web.

Em resumo: Configure um socks5 com shadowsocks-libev, escutando na máquina local em 172.17.0.1 (não localhost), passe as informações do proxy conforme sua mensagem e reconstrua o app.

Vou escrever um guia detalhado aqui, pois presumo que haja mais pessoas passando por essa experiência dolorosa. No momento, ainda estou enfrentando problemas com repositórios de componentes de tema, então minha rebuild ainda não teve sucesso, mas já superei pelo menos todas as buscas de plugins.

Um pouco fora do tópico principal, mas a dor que estou enfrentando não é a de não conseguir iniciar o app, pois a configuração existente do redis-server — que temos em outra máquina — não corresponde à realidade do status atual do app. Assim, não consigo iniciar o container e desativar os componentes de tema via GUI, que enfrentam um timeout ao cloná-los.

Muito obrigado por me direcionar à sua explicação, mas gostaria de adicionar algumas observações, já que o exemplo não se encaixa perfeitamente.

  1. Não entendi isso, desculpe? O comando env me fornece muitas informações, mas nenhuma relacionada ao meu gitconfig.
  2. Como não entendi o ponto 1, não consegui identificar quais variáveis passar. Também não adicionei as flags do git à seção env no app.yml, mas as chamei via um hook.
  3. Isso não foi necessário, pois não quero que todo o container passe pelo proxy SOCKS, mas apenas o processo git fetch. No entanto, suponho que esse ponto fosse mais específico para o caso de uso original no tópico ao qual você se referiu.

Mas, novamente, muito obrigado. Sua contribuição me apontou na direção certa. Agradeço à equipe do Discourse! :ok_hand:

1 curtida

Você comprou o Aliyun e selecionou uma região na China continental?

O Aliyun de HK/internacional não terá esse problema.

1 curtida

Além disso, talvez você já tenha discutido esse detalhe, mas caso não tenha encontrado, aqui está um script que você pode executar para instalar o git via openssl

Atualização manual sem dor de cabeça dentro da China

passos

  1. criar um proxy SOCKS5 fora da China
  2. configurar e estabelecer a conexão do proxy no servidor CN
  3. criar um modelo para facilitar a edição
  4. adicionar configurações de proxy git ao modelo
  5. incluir o modelo em app.yml
  6. reconstruir o app

1 - SOCKS5 remoto

Para facilitar o uso (e devido aos preços amigáveis), recomendo configurar um servidor Digital Ocean, por exemplo, em Cingapura. Basta usar um servidor Ubuntu padrão, passar por todas as configurações básicas de segurança (pares de chaves SSH, UFW, etc.) e, em seguida, instalar o Shadowsocks:

na máquina remota
$ sudo apt install shadowsocks-libev

Configure as configurações do proxy:

$ cd /etc/shadowsocks-libev

# Gosto de manter os arquivos originais
$ sudo cp config.json orig.config.json
$ sudo nano config.json

Preste muita atenção ao tempo limite (timeout) e ao método:

{
    "server":"123.123.123.123", # IP do servidor remoto
    "server_port":8400, # fica a seu critério
    "local_port":1080,
    "password":"Swordfish", 
    "timeout":600, # <= essencial!
    "method":"chacha20-ietf-poly1305"
}

Certifique-se de verificar novamente todas as configurações na configuração do systemd (/lib/systemd/system/shadowsocks-libev-local@.service). Ative o shadowsocks-libev-local@.service, reinicie e verifique se o serviço está em execução.

2 - configurar a conexão do proxy no servidor CN

na máquina Discourse

$ sudo apt install shadowsocks-libev

Se estiver na Aliyun, procure as configurações do Firewall no console deles (que é estranho) e verifique as configurações de porta respectivas.

Você não precisa se preocupar com as configurações do systemd na máquina cliente, mas mantenha arquivos de configuração separados para o Docker e uso regular, pois você pode querer usar o proxy SOCKS5 fora do contexto do Docker; nesse caso, você usaria 127.0.0.1 em vez dos endereços de rede acessíveis pelo Docker.

$ cd /etc/shadowsocks-libev
$ sudo cp config.json local.json
$ sudo cp config.json docker.json

adapte a configuração para algo semelhante a isto

$ sudo nano local.json

{
    "server":["123.123.123.123"], # o IP da máquina remota
    "mode":"tcp_and_udp", # esta anotação é diferente devido às versões diferentes do shadowsocks-libev na minha configuração
    "server_port":8400,
    "local_address":"127.0.0.1",
    "local_port":1080,
    "password":"Swordfish",
    "timeout":600, # <= certifique-se disso
    "method":"chacha20-ietf-poly1305"
}

Por conveniência, vamos adicionar um alias ao nosso .bashrc :

$ nano ~/.bashrc

# cole
alias dockershadow='ss-local -c /etc/shadowsocks-libev/local.json'

adapte a outra configuração para permitir que o Docker use a rede da máquina host

$ sudo nano docker.json

{
    "server":["123.123.123.123"],
    "mode":"tcp_and_udp",
    "server_port":8400,
    "local_address":"172.17.0.1",
    "local_port":1080,
    "password":"Swordfish",
    "timeout":600,
    "method":"chacha20-ietf-poly1305"
}

defina o alias para usar a configuração específica do Docker:

alias dockershadow='ss-local -c /etc/shadowsocks-libev/docker.json'

3 & 4 - criar um modelo para manter seu app.yml organizado

Isso é absolutamente opcional e depende do seu gosto; eu prefiro manter o app.yml legível e curto, mantendo os componentes em outro lugar. Dê qualquer nome de acordo com seu gosto; eu escolhi web.git.template.yml.

$ nano templates/web.git.template.yml
# cole:

hooks:
  before_code:
    - exec:
       cmd:
         - git config --global http.proxy socks5://172.17.0.1:1080
         - git config --global https.proxy socks5://172.17.0.1:1080
         - git config --global https.sslVerify = false 

# opcional
  after_code:
    - exec:
       cmd:
         - git config --global --unset http.proxy
         - git config --global --unset https.proxy
         - git config --global --unset https.sslVerify

Testei com o hook after_web, mas isso não funcionou.

5 - adaptar o app.yml

Chame o modelo no seu app.yml:

$ cd /<discourse dir>
$ sudo nano containers/app.yml


templates:
  - "templates/web.template.yml"
  - "templates/web.china.template.yml"
  - "templates/web.ratelimited.template.yml"
  - "templates/web.socketed.template.yml"
  - "templates/web.git.template.yml"

Sua seção de modelos provavelmente parece diferente; apenas certifique-se de incluir web.china e os modelos web.git-blabla (ou como quer que você os tenha nomeado).
Não exponha 1080:1080 no seu app.yml!

6 - reconstrução

Antes de reconstruir, verifique se suas configurações de proxy funcionam ao clonar com git.

$ git config --global http.proxy socks5://172.17.0.1:1080
$ git config --global https.proxy socks5://172.17.0.1:1080
$ git config --global https.sslVerify = false 

Isso, claro, adiciona as flags de proxy ao .gitconfig do usuário no diretório home, então tenha cuidado para removê-las após os testes.
Selecione um repositório grande e aleatório no Github com muitos arquivos e verifique sua velocidade de clonagem. Se sua configuração estiver correta, você deverá conseguir clonar com ~12-15 MB/s, dependendo da sua configuração da Aliyun. Se sua velocidade de conexão subir lentamente de 200 KB/s para cerca de 10 MB/s, então seus esforços não foram bem-sucedidos.

finalmente, reconstrua:

$ cd /<discourse directory>

# execute o proxy usando o alias que definimos antes
$ dockershadow
$ ./launcher rebuild app

O processo de reconstrução falhará frequentemente, então você precisará de paciência (e possivelmente Baijiu). Quanto menos plugins você tiver configurado no seu app.yml, mais provável será que sua reconstrução tenha sucesso.

7 - observações

Ainda considero isso uma solução alternativa, não um procedimento pronto para produção, então talvez alguém tenha uma ideia de como espelhar o repositório do GitHub na China, tornando isso menos doloroso. E, como todos sabemos, os mecanismos pouco transparentes dentro do GFW continuam mudando.

Claro, um proxy SOCKS5 é apenas uma das muitas opções, mas gosto de ter soluções de uso múltiplo à mão.

Se alguém tiver uma ideia de como tornar essa solução alternativa pronta para produção, agradeço sua contribuição. O Discourse é um software fantástico, mas presumo que uma das razões para não ser amplamente usado na China sejam os processos de instalação e manutenção complicados. Tentar atualizar via GUI me deu uma taxa de falha de 100% no último ano, não importa quais configurações de tempo limite eu tenha configurado no meu proxy reverso nGinx.

Tradução para o chinês seguirá

7 curtidas

Exatamente. Como o objetivo principal dessa instância é fazer parte de uma estrutura de intranet corporativa, HK, infelizmente, não é uma opção devido à latência. Além disso, as instâncias (em breve) voltadas para clientes lidarão com usuários da China continental — assim que eu resolver a autenticação do Weixin —, então preciso de uma solução viável para as zonas do Aliyun na China continental.

Muito obrigado. Verifiquei vários guais sobre isso, mas como a principal causa do problema não é a autenticação TLS do Git em si, mas a verificação de handshake nos processos de inspeção de pacotes do GFW, abstenho-me dessa abordagem. Compilar o git com openssl pode abrir portas para um novo mundo cheio de dor, como eu havia lido.

A maioria dos componentes do tema também é baixada do GitHub durante a compilação (ou ao iniciar o container?), então talvez haja outro hook para adicionar o proxy git que possa ajudar. Não remova o proxy se quiser que funcione com a GUI. E o redis-server não parece ser a causa disso.

O redis-server foi apenas mais um problema que adicionou complexidade à falha na reconstrução. Era meio que um ciclo: a configuração externa do redis havia mudado, enquanto o estado do aplicativo pré-reconstrução precisava dessa configuração específica do redis para iniciar. Não foi possível reconstruir, pois a busca dos componentes do tema não funcionava.

No entanto, tive sorte após 20-20 execuções de reconstrução e, finalmente, as atualizações dos componentes do tema foram buscadas.

No contexto geral do design do aplicativo, seria bom ter documentação sobre como reconstruir em um “modo seguro”, ou seja, reconstruir o aplicativo independentemente de plugins ou temas. Não consegui encontrar um hook para processar os componentes do tema, nem como desativar (em contraste com desinstalar) plugins, o que foi uma pena.

Edição: Uau, agora vejo o link para o modo seguro. Não encontrei isso antes (sem Google para nós na China, tente encontrar qualquer coisa relevante com o Bing…). Meu Deus, isso teria ajudado muito!

Então você especificou um servidor Redis gerenciado como Discourse with DO managed Redis - #3 by Falco e não conseguiu reconstruir?

O problema do Redis era de importância secundária, mas adicionou complexidade significativa ao problema geral do Git. Como você pode notar em minha postagem detalhada acima, resolvi os problemas.

E sim, desde o início conectamos um cluster distribuído de Redis ao nosso Discourse. No entanto, ele não é gerenciado, apenas está em outras máquinas.

Uma falha na conexão com o servidor Redis impediu que o aplicativo fosse iniciado, portanto não pude desativar os componentes do tema na interface gráfica.
Aplicar uma nova configuração do Redis exigia uma reconstrução do aplicativo, o que não pôde ser feito devido à falha ao buscar nos repositórios do GitHub.

https://meta.discourse.org/t/a-fork-of-discourse-docker-repo-for-china

1 curtida

Caso alguém encontre problemas mesmo com a configuração do proxy HTTP adicionada,

GnuTLS recv error (-110): A conexão TLS foi terminada de forma inadequada.

Além da solução original, adicione as propriedades postBuffer abaixo a um template para resolver meu problema. gnutls-bin é necessário para instalar

hooks:
  before_code:
    - exec:
       cmd:
         - apt-get update -y
         - apt-get install -y gnutls-bin
         - git config --global http.proxy socks5://172.17.0.1:1080
         - git config --global https.proxy socks5://172.17.0.1:1080
         - git config --global https.sslVerify false
         - git config --global http.postBuffer 1048576000

# opcional
  after_code:
    - exec:
       cmd:
         - git config --global --unset http.proxy
         - git config --global --unset https.proxy
         - git config --global --unset https.sslVerify
         - git config --global --unset http.postBuffer
2 curtidas