Aspnetcore: IIS -> Bloquear arquivo DLL do aplicativo .NET Core

Criado em 7 jul. 2016  ·  114Comentários  ·  Fonte: dotnet/aspnetcore

Quando tento sobrescrever o arquivo DLL do aplicativo .NET Core usando FTP na produção, o arquivo do servidor IIS está bloqueado e não pode ser sobrescrito.

Para implantar uma nova versão, preciso interromper o aplicativo no IIS para liberar o bloqueio e, em seguida, substituir.
Não é possível implantar sem parar o aplicativo.

affected-medium area-servers bug servers-iis severity-nice-to-have

Comentários muito úteis

👍

Não apoiar a reciclagem sobreposta é uma regressão.

Todos 114 comentários

Consulte app_offline.htm: https://docs.asp.net/en/latest/hosting/aspnet-core-module.html#asp -net-core-module-app-offline-htm

Se você desejar uma maneira do PowerShell de fazer isso, poderá usar os cmdlets de administração do IIS: https://technet.microsoft.com/en-us/library/ee790599.aspx

Stop-WebAppPool -Name $appPoolName

... deploy ...

Start-WebAppPool -Name $appPoolName

Obrigado pela explicação @Tratcher .
Não tenho certeza se isso está em seus planos, mas funcionou para ASP.NET MVC anterior, eu acho?
Você planeja implementar esse recurso ou não?

@GuardRex Não posso fazer isso porque é um ambiente de hospedagem compartilhada e não tenho permissão para parar o AppPool

Você pode fazer com que ele 'simplesmente funcione' com msdeploy.exe e Azure? Se bem entendi, o site precisa ser reiniciado para evitar o bloqueio de arquivos. -enableRule:AppOffline funciona, mas todo o site fica offline por alguns minutos, o que não é uma ótima experiência do usuário, especialmente considerando que implementamos algumas vezes por dia.

Veja também http://stackoverflow.com/q/40276582/14131

@chuchuva talvez, mas toda mágica tem um custo. As versões anteriores do ASP.NET faziam algumas cópias de sombra complicadas para contornar os problemas de bloqueio de arquivo.

Mágico ou não, funcionou bem. Eu meio que sinto falta agora, após a migração para o núcleo ASP.NET ...

Acordado com @HarelM. Encontramos problemas com isso, desde nossas implantações automatizadas até a experiência do usuário final. Deixamos de implantar cerca de 10 vezes por dia com o antigo MVC para, talvez, uma implantação diária à noite, aceitando que as pessoas que usam o aplicativo Core ficarão irritadas quando o colocarmos offline. Embora não seja um obstáculo, é um atrito adicional para a adoção do Core.

👍

Não apoiar a reciclagem sobreposta é uma regressão.

Há planos de revisitar esse recurso e colocá-lo no roteiro? É muito inconveniente / hostil exigir que todos que implantam um site .Net Core implementem manualmente uma estratégia de slot de teste se quiserem oferecer suporte a implantações com tempo de inatividade zero.

Ter esse recurso tornaria a transição para sites .Net Core muito mais suave para muitas pessoas e permitiria uma adoção mais rápida de sites .Net Core.

Gostaríamos de saber também. Colocar um app_offline.htm dentro do diretório de aplicativos não funciona.

Acabei de perceber esse 'recurso' depois de mover vários sites para o AspNetCore. Não acredito que seja aceitável colocar seus sites off-line por alguns minutos toda vez que você quiser publicá-los!

Isso é ruim o suficiente para mim, como líder de uma pequena equipe - aqueles que praticam a integração contínua em grande escala estão evitando o AspNetCore? Não há como eles deixarem seu site offline por minutos a cada hora para republicá-lo!

Você está implantando usando FTP ou xcopying? Ou você está usando o webdeploy?

Estou publicando via webdeploy no IIS.

No momento, estamos resolvendo esse problema apenas excluindo o web.config primeiro (efetivamente encerrando o aplicativo), mas essa não é realmente uma solução aceitável a longo prazo.

@DaleMckeown idealmente com um fluxo de trabalho de integração contínua, você teria vários servidores atrás de um balanceador de carga. Em seguida, você puxaria um servidor, o atualizaria, o colocaria de volta e passaria para o próximo. Claro que nem sempre isso é possível (como no nosso caso), então você terá que conviver com alguns minutos de inatividade. Em nosso caso, o backup do aplicativo ocorre em 30s, então isso não é realmente um problema.

@DaleMckeown

Estou publicando via webdeploy no IIS.

Existem algumas opções que podem ser usadas para fazer a implantação usando o webdeploy funcionar bem (ele oferece suporte à renomeação de arquivos bloqueados e ao descarte do aplicativo off-line automaticamente). É esse o caso por padrão @shirhatti ?

@ajeckmans

No momento, estamos resolvendo esse problema apenas excluindo o web.config primeiro (efetivamente encerrando o aplicativo), mas essa não é realmente uma solução aceitável a longo prazo.

Isso significa que você está fazendo uma implantação xcopy?

Existem algumas opções que podem ser usadas para fazer a implantação usando o webdeploy funcionar bem (ele oferece suporte à renomeação de arquivos bloqueados e ao descarte do aplicativo off-line automaticamente). É esse o caso por padrão @shirhatti ?

Obrigado David - parece melhor do que o que estou fazendo atualmente (parando manualmente o pool de sites e aplicativos no IIS). Você pode apontar alguns recursos para que eu possa investigar as implicações dessas abordagens?

@DaleMckeown algumas informações até que eu encontre a fonte da verdade https://github.com/Microsoft/vsts-tasks/issues/5259#issuecomment -346202503

@davidfowl Estamos usando o webdeploy para implantar em nosso servidor de testes (o que btw nunca nos dá problemas), de lá copiamos os arquivos para nossos servidores ativos usando robocopy

@ajeckmans

No momento, estamos resolvendo esse problema excluindo o web.config primeiro

Quando o web.config é removido da implantação, o IIS servirá arquivos confidenciais da implantação. Um invasor pode simplesmente solicitar arquivos continuamente, dia e noite, até que a janela dos anos 30 seja aberta.

@DaleMckeown

Seguindo o conselho de @ajeckmans, se você quiser se https://github.com/guardrex/aspnetcore-iis-ps-publish ... mas veja isso como um exemplo experimental e não um script de qualidade de produção. Eu não brinquei com isso por um tempo, mas deveria (em teoria) ainda funcionar.

@guardrex você está certo. Copiamos um app_offline.htm para o dir primeiro, então rememoramos o web.config, copiamos o aplicativo, colocamos de volta o web.config e removemos o app_offline (tudo com um script ofc). Infelizmente, apenas colocar o arquivo app_offline no diretório de sites não interrompe o bloqueio das dlls. Precisamos remover o web.config, uma ação que não precisamos fazer para os aplicativos completos do asp.net mais antigos (como formulários da web antigos, etc.)

@ajeckmans Isso não deve funcionar. Quando o arquivo web.config é removido, o IIS seleciona essa alteração imediatamente, então ele deve usar as solicitações padrão para o Módulo de arquivo estático e começar a servir os arquivos. Experimente e veja se é isso que acontece ...

  1. Adicione app_offline.htm .
  2. Confirme se o site exibe app_offline.htm .
  3. Retire o web.config .
  4. Solicite um arquivo confidencial (por exemplo, http://localhost:<PORT>/<ASSEMBLY_NAME>.deps.json ... substituindo PORT e ASSEMBLY_NAME).
  5. Você deve obter o arquivo deps.json servido se o seu Módulo de arquivo estático estiver funcionando corretamente.

Eu não confiaria apenas em remover o módulo com ...

<configuration> 
 <system.webServer> 
   <modules> 
     <remove name="StaticFileModule" /> 
   </modules> 
 </system.webServer> 
</configuration>

... já que outros módulos permaneceriam e possivelmente apresentariam outros vetores de ataque. Talvez seja possível remover todos os módulos não essenciais, mas estamos entrando em águas desconhecidas com um movimento como esse apenas para cobrir uma implantação por meio da remoção de web.config . Isso nunca foi uma opção sob a orientação oficial e, portanto, é totalmente incompatível. Eu recomendo o script PS, por exemplo, para derrubar o AppPool ou alguma outra estratégia.

Acho que devo acrescentar uma nota de simpatia a isso ... levantou algumas sobrancelhas do ponto de vista da segurança. Quando essa mudança de layout de implantação foi feita, ela foi discutida, incluindo colocar o arquivo de volta em wwwroot , veja ...

Discussão para: Publicar para alterações do IIS no local web.config (IISIntegration 158)
Verifique ao mover web.config volta para wwwroot (IISIntegration 164)

[EDITAR] Talvez essa seja a sua solução alternativa (sem suporte): Mova o web.config de volta para wwwroot e prossiga com o que está fazendo. Mesmo assim ... meio assustador quebrar o aplicativo para colocá-lo offline assim.

Isso me deixa ainda mais confuso sobre o que devemos fazer nesta situação.

Qual é a recomendação atual para publicar sites AspNetCore no IIS por meio de implantação na web de uma forma que possa evitar bloqueios de arquivos?

Eu gostaria de saber melhor. Isso está se tornando um grande obstáculo para a adoção do núcleo .net em minha organização.

Quando meu aplicativo da web principal .net está em execução e estou tentando publicar uma nova compilação, recebo um erro de que as DLLs estão em uso. Portanto, tenho que interromper o pool de aplicativos, atualizar as DLLs e iniciar o pool de aplicativos.

Existe alguma solução alternativa no .Net Core pela qual posso publicar o novo Build / DLLs sem interromper o App Pool?

Por padrão, isso é compatível com a estrutura .Net usando o mecanismo de cópia de sombra. Infelizmente, já se passaram 2 anos desde o lançamento do núcleo .Net e parece que ainda não há suporte para essa funcionalidade tão básica e necessária. Existe algum plano para consertar isso no futuro?

Agradecemos antecipadamente por qualquer ajuda.

@guardrex , é realmente assustador quebrar o aplicativo para obter o IIS (ou o que quer que esteja travando) para liberar as DLLs, no entanto, é ainda mais assustador não ser capaz de enviar hotfixes para um produto. Por enquanto, quebrar o aplicativo é a única coisa que podemos fazer, mas estamos procurando outras abordagens mais modernas para lidar com isso :)

@ shahjay748 não prenda a respiração. Se eu fosse refazer o processo aqui, colocaria o aplicativo dentro de um contêiner e apenas colocaria um novo contêiner e alternaria o tráfego e puxaria a versão antiga para baixo (ou algo parecido). Parece a coisa mais sensata a fazer e, visto que o núcleo .net tem tudo a ver com novas formas modernas, apenas empurrar uma nova versão do aplicativo simplesmente copiando os novos arquivos é considerado (e eu concordo) uma forma um pouco misteriosa para fazer isso.
Qual ofc não nos ajuda que estamos, por enquanto, presos a processos antigos :)

O que eu faço (não tenho certeza se é uma maneira correta de fazer isso), é enviar o site para outro diretório e alternar caminhos no iis

Fizemos algumas investigações internas sobre este problema. Pelo que posso dizer, o ANCM não contém nenhum arquivo / identificador no aplicativo implantado. Parece ser um problema com o próprio webdeploy e ele falha na implantação. Nunca tive uma falha consistente ao implantar o aplicativo depois de tentar novamente algumas vezes depois que o app_offline foi descartado.

Adicionando uma nota lateral rápida do PowerShell a isso: O bloqueio no assembly do aplicativo é sempre liberado ... Eu nunca tive um problema ( ainda! Lol).

@ bpetersen1 ... Sua abordagem de "teste" tem o doce efeito colateral de oferecer uma opção de reversão rápida se a nova implantação falhar. Isso deve ser fácil de escrever, se necessário.

Isso ainda é externamente irritante ... decidimos procurar uma solução docker para deixar o IIS para trás.

Vou experimentar uma solução. Fique ligado.

Também estou tendo esse problema ao usar o FTP.

Qual é o conselho da Microsoft sobre isso ?!

Também estou tendo esse problema ao usar o FTP.

Qual é o conselho da Microsoft sobre isso ?!

Solte um arquivo appoffline.htm, ftp e remova-o.

@davidfowl Sim, é isso que estou fazendo agora com um arquivo em lote. Demora alguns segundos para o IIS liberar o bloqueio e, em seguida, copiar os arquivos.

Obrigado - sem interesse, como você está executando o arquivo em lote?

Também apreciamos um link para a documentação da MIcrosoft sobre isso; Não consigo encontrar no google. Obrigado.

@ niico100 Basicamente, o arquivo em lote apenas copia os arquivos para o diretório do projeto e eu
Causa o problema de bloqueio de arquivo, a cópia fará uma nova tentativa. Estou usando robcopy
https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy

Se alguém estiver interessado, o seguinte script baixa o arquivo de implantação do appveyor, interrompe o site, copia os arquivos necessários e traz o site de volta:
https://github.com/IsraelHikingMap/Site/blob/master/Scripts/Deploy.ps1
Porém, ele precisa ser executado como administrador. espero que o docker resolva todo esse absurdo assim que migrarmos ...
ele usa os comandos Stop-WebAppPool e Start-WebAppPool powershell.

Para desenvolvimento local, WebDeploy não funcionou para mim. Tentei usá-lo em meu IIS local e ele ainda reclamava de arquivos bloqueados. Em seguida, tentei usar os commandlets do PowerShell acima dos eventos de compilação do VS Pré / Pós, mas isso também não funcionou para mim, provavelmente porque o PowerShell de 32 bits não pode modificar as configurações do IIS de 64 bits. De qualquer forma, o que parece funcionar bem são os seguintes eventos de pré / pós-compilação em meu projeto ASP.Net Core:

Evento de pré-construção:
echo "App Offline" /a > $(ProjectDir)app_offline.htm

Evento pós-construção:
del $(ProjectDir)app_offline.htm

Eu li brevemente as reclamações aqui, e parece que isso pode estar relacionado. Mudei as implantações de CI para nós de implantação de octopus que geralmente "apenas funcionou", para a utilização de lançamentos de devops azure com as tarefas de gerenciamento iis, e ainda frequentemente tenho problemas para implantar novos arquivos porque mesmo depois de parar o webapp, parando o domínio do aplicativo, os arquivos ainda estão em usar.

Isso é provavelmente pelo menos 1 em cada 3 implementações e não encontrei nada que pareça melhorar a situação ou implementações mais confiáveis.

@Tratcher @ronnyek Isso parece estar relacionado ao Application Insights; depois de adicionar a extensão do App Insights, meu aplicativo da web DLL de repente ficou muito difícil de atualizar, sem problemas antes disso.

Isso faria sentido, visto que o App Insights deve conectar o criador de perfil ao processo do aplicativo e às DLLs.

Ainda estou enfrentando esse problema. Eu uso o sinalizador -e nableRule: AppOffline , às vezes está funcionando bem, mas na maioria das vezes a implantação está falhando como "ERROR_FILE_IN_USE".

Depois de iniciar uma versão, eu monitoro a pasta do aplicativo da web e vejo o App_Offline.html descartado pelo msdeploy. Em seguida, o lançamento falhou com a mensagem acima e observe que o arquivo App_Offline ainda está na pasta, eu inicio o lançamento novamente, desta vez está funcionando bem porque o módulo vê o arquivo na pasta.

último tempo de execução do núcleo do asp.net instalado -> aspnetcore.dll 12.1.18263.2

Alguma ideia ? Ainda é um bug?

@dhtek Se você tentar substituir os arquivos imediatamente após colocar app_offline.htm , então o servidor simplesmente não teve tempo suficiente para encerrar o aplicativo. Você deve esperar um pouco antes de tentar substituir os arquivos.

Ok, entendo, mas estou usando a opção -e nableRule: AppOffline do msdeploy e a página é automaticamente solta na pasta e a sincronização é feita imediatamente. Eu não faço nada sozinho.

Ainda estou vendo esse problema. Meus arquivos de projeto estão fazendo referência a Microsoft.NET.Sdk.Web , mas não estão descartando um arquivo app_offline.htm automaticamente, como declarado na documentação.

Oi pessoal. Este problema irritante ainda está presente com o Core 2.1 com o último VS 2017. Eu uso appoffline, ainda que a dll do projeto principal esteja sempre em uso e requer uma parada completa do site durante a publicação. Isso torna a implantação da web quase inútil.

Se você não pode colocar seu host offline atrás do balanceador de carga e gostaria de fazê-lo mais rápido, aqui está uma maneira com links simbólicos e algum powerhell.
Eu estava pensando em fazer algo assim.

É mais rápido, pois você não precisa interromper o pool de aplicativos durante a cópia + reciclagem, é melhor do que uma parada brusca, pois permite que as solicitações atuais sejam concluídas, reduzindo o tempo de inatividade.

# Setup
Import-Module WebAdministration
# create 2 site root directories
$a = 'C:\inetpub\AspNetCoreSampleA'
$b = 'C:\inetpub\AspNetCoreSampleB'
$siteRoot = 'C:\inetpub\aspnetcoresample'
$siteName = 'AspNetCoreSample'
$poolName = "aspnetcore"
New-Item -Type Directory $a
New-Item -Type Directory $b
# create a symlink to targeting one side
New-Item -Type SymbolicLink -Path $siteRoot -Target $a
# point the site root to the symlink
Set-ItemProperty "IIS:\Sites\$siteName" -name physicalPath -value $siteRoot
# make sure it get's picked up
Restart-WebAppPool -Name $poolName

# this tells you the active side
Get-Item -Path $siteRoot | Select-Object -ExpandProperty target

# Flip the symlink
$current = (Get-Item -Path $siteRoot).Target
$newTarget = if ($current -eq $a) {$b} else {$a}
New-Item -Type SymbolicLink -Path $siteRoot -Target $newTarget -Force
# at this point w3wp.exe still locks the current target folder until it's getting recycled
# Deploy new version to the symlink which is now pointing to the other side which should have no locks
robocopy \\myshare\myapp $siteRoot /mir
# recycle app pool, so it picks up the new files
Restart-WebAppPool -Name $poolName

# bonus point: rollback is easy
$current = (Get-Item -Path $siteRoot).Target
$newTarget = if ($current -eq $a) {$b} else {$a}
New-Item -Type SymbolicLink -Path $siteRoot -Target $newTarget -Force
Restart-WebAppPool -Name $poolName

Aqui está a essência
https://gist.github.com/csharmath/b2af0f50700ce9fbdd8c5c3e582fd41b

Restart-WebAppPool é basicamente reciclar, o que é útil se você tiver a reciclagem sobreposta ativada (o padrão), pois isso gerará um novo w3wp.exe e todas as novas solicitações serão atendidas por esse novo processo enquanto as atualmente em execução serão concluídas pelo antigo w3wp.exe.
Dessa forma eles vão se sobrepondo até que o antigo seja feito com as solicitações, você acaba com um único w3wp.exe apontando para a nova versão e não há problema de bloqueio.

Isso é semelhante à cópia de sombra, mas não 100%, pois fornece um cenário de xcopy muito mais agradável e perfeito.

Até agora, parece ser a melhor abordagem que posso pensar se você precisar ficar online durante um lançamento.

@csharmath ou também há docker, eu acho. Nenhum arquivo é bloqueado dessa maneira, e ele pode fazer atualizações contínuas via swarm / k8s / okd

Claro, isso é ainda melhor, é claro. suspiro, nem todo lugar tem isso ainda.
Para o resto das configurações antigas, você precisa ter o yakshave até que funcione para você, com nenhum ou mínimo tempo de inatividade.

@csharmath Concordo, mas neste ponto, dado que este tem sido um problema não resolvido por _anos_, pode muito bem se adaptar e ir atrás de soluções que funcionem em vez de esperar por um milagre ...

Eu usei o script power shell abaixo e travou o IIS e até afetou o outro pool de aplicativos e eu tenho que reiniciar a máquina para obter tudo de volta ... este processo eu peguei do documento oficial e foi um desastre, então meu conselho é NÃO USE O app_offline.htm melhor para usar o script que interrompe o pool de aplicativos e inicie-o novamente, um que encontrei neste tópico https://github.com/IsraelHikingMap/Site/blob/master/Scripts/Deploy .ps1

$pathToApp = 'G:\prod_web_core'
$pathToAppOfflineHtml = 'G:\prod_web_core\app_offline.htm'
# Stop the AppPool 
New-Item -Path $pathToApp -Name app_offline.htm
# Provide script commands here to deploy the app
Copy-Item "G:\prod_web_core_temp\*" -Destination $pathToApp -Recurse -Force
# Restart the AppPool 
Remove-Item -Path $pathToAppOfflineHtml
Get-ChildItem -Path "G:\prod_web_core_temp" -Recurse | Remove-Item -Force

Eu sou forçado a parar o AppPool para implementação bem-sucedida, algum progresso para corrigir esse problema ?!

Acabamos de encontrar esse problema de bloqueio de arquivo pela primeira vez hoje, uma vez que o núcleo dotnet é novo para nós. Eu realmente fui pego de surpresa por isso. É engraçado que a razão número 1 pela qual mudei do Classic ASP para .Net Framework enquanto ele ainda estava em beta (~ 2001) é porque eu poderia fazer implantações dinâmicas sem bloqueio de arquivos. Foi demais! Quase 18 anos depois e agora estou de volta onde comecei. Bem, você ganha alguns, você perde alguns.

Acabei de mencionar meu problema ... no meu caso, implantação de soluções Blazor Server Side ....
Alguns dias atrás, foi um pesadelo ... Apliquei o app_offline.htm, fechei pools de aplicativos e até reiniciei instâncias inteiras do IIS para desbloquear arquivos. O próximo passo seria reiniciar todo o servidor, felizmente após 5 minutos os .dlls se desbloquearam.

2019, mesmo problema para mim ... e esse problema foi criado em 2016 ... ainda sem solução?

2020, mesmo problema para mim. A maior regressão de todos os tempos. .Net 4.6 você substitui os arquivos dll e o aplicativo recarrega na nova versão. .Net core bloqueia tudo.

@bladefist Sua melhor aposta a partir de hoje é usar contêineres Docker: /

@kanadaj obrigado, mas Dockers é outro nível de dores de cabeça para lidar. Acabei descobrindo uma solução que seria usar App_offline.htm e depois esperar um minuto e esperar que os arquivos sejam gratuitos. 99% das vezes, você só tem que esperar, acho que meu aplicativo demora um pouco para desligar.

Já se foi o tempo do .net 4.6, em que você apenas copiava suas dlls e seu aplicativo era reciclado automaticamente. Acho que era muito simples. O que é duplamente frustrante é que isso não é um problema no Linux. Você pode substituir arquivos em uso no Linux.

Bem, sim, eu acho, mas esse método não é muito simples em um ambiente de produção. Sem HA, seu aplicativo acabará não respondendo por um minuto, e com HA você recebe algumas solicitações ruins aleatórias até que o balanceador de carga descubra que o aplicativo está inativo ... A menos que você execute um ciclo manual de hosts em seu balanceador de carga. Mas isso parece ainda mais difícil de configurar.

@kanadaj sim, usamos um balanceador de carga e ele atualiza os servidores linearmente. Sem tempo de inatividade.

Tivemos sucesso com

  • Uso de um link simbólico para o diretório do aplicativo
  • Implante uma nova versão do site em um diretório diferente
  • Atualize o link simbólico
  • Reinicie o pool de aplicativos usando o utilitário appcmd que vem com o IIS.

@davidglassborow é uma solução criativa. Obrigado. Porém, soluções criativas não deveriam ser necessárias.

Na minha experiência, o mecanismo antigo não era confiável quando o site estava sobrecarregado, de qualquer forma acabamos colocando proxies reversos para sites de alto tráfego.

Certo, as implantações no local nunca foram confiáveis ​​ou atômicas, mas se você tiver sorte ou não tiver muito tráfego, tudo bem. Presumo que a maioria das reclamações seja em torno desses cenários. Veremos como melhorar as coisas no período do .NET 5.0. Algumas advertências:

  • Veremos como tornar as coisas melhores para aplicativos dependentes de framework. Se você implantar autocontido, você está sem sorte. Ele bloqueará os arquivos nativos e gerenciados na pasta de saída. O problema de bloqueio de dlls nativos sempre existiu e não será corrigido por qualquer mitigação que colocarmos em prática.
  • Planejamos olhar para carregar assemblies de aplicativos na memória como bytes, em vez de carregá-los do disco e bloquear o arquivo. Isso deve funcionar na maioria dos casos, mas pode resultar em mais uso de memória em alguns casos.
  • Para reciclar o aplicativo, você ainda precisa adicionar e remover um arquivo appoffline. Adicionar o arquivo appoffline é uma forma de sinalizar que você deseja que o aplicativo reinicie como resultado da implantação.

@davidfowl O plano que você delineou para o 5.0 parece ótimo. Nós usamos o framework dependente como tenho certeza que a maioria faz.

Discordar respeitosamente sobre a confiabilidade das implantações no local. Tenho feito isso há anos em sites com carga extremamente alta, nunca tive um problema, nem uma vez. A primeira alteração detectada faz com que o aplicativo seja redefinido, o balanceador de carga vê que a instância não está respondendo e a coloca offline, uma vez que todas as DLLs são substituídas, o balanceador de carga conclui uma verificação de integridade e volta a ficar online. Agora, se não houvesse verificação de integridade do balanceador de carga, então sim, é uma aposta, mas quero dizer quem está substituindo DLLs em produção sob alta carga sem um proxy de algum tipo? Vamos nos concentrar na regra e não na exceção. Sem um proxy, não consigo pensar em nenhuma maneira de atualizar o aplicativo sem causar uma interrupção. Mesmo se você suportasse recarregamento a quente, algumas solicitações provavelmente travariam ou falhariam.

Discordar respeitosamente sobre a confiabilidade das implantações no local. Tenho feito isso há anos em sites com carga extremamente alta, nunca tive um problema, nem uma vez. A primeira alteração detectada faz com que o aplicativo seja redefinido, o balanceador de carga vê que a instância não está respondendo e a coloca offline, uma vez que todas as DLLs são substituídas, o balanceador de carga conclui uma verificação de integridade e volta a ficar online. Agora, se não houvesse verificação de integridade do balanceador de carga, então sim, é uma aposta, mas quero dizer quem está substituindo DLLs em produção sob alta carga sem um proxy de algum tipo? Vamos nos concentrar na regra e não na exceção. Sem um proxy, não consigo pensar em nenhuma maneira de atualizar o aplicativo sem causar uma interrupção. Mesmo se você suportasse recarregamento a quente, algumas solicitações provavelmente travariam ou falhariam.

Você tem um balanceador de carga. Muitas pessoas não têm um balanceador de carga e estão apenas substituindo os arquivos no disco e esperando mágica 😄

@davidfowl Certo, mas tráfego intenso + sem lb = imprudente

Além disso, o problema com app_offline.htm é que em um processo ci / cd você não tem ideia de quando o processo é interrompido. Tivemos que adicionar um comando de suspensão enorme entre criar esse arquivo e enviar as DLLs porque nosso processo de desligamento pode levar de 1s a 1 minuto. Quando você faz o balanceamento de carga de toneladas de instâncias, essas interrupções resultam em um ciclo de implantação muito longo.

Carregar os arquivos na memória corrige tudo isso.

@davidfowl Certo, mas tráfego intenso + sem lb = imprudente

👍

Além disso, o problema com app_offline.htm é que em um processo ci / cd você não tem ideia de quando o processo é interrompido. Tivemos que adicionar um comando de suspensão enorme entre criar esse arquivo e enviar as DLLs porque nosso processo de desligamento pode levar de 1s a 1 minuto. Quando você faz o balanceamento de carga de toneladas de instâncias, essas interrupções resultam em um ciclo de implantação muito longo.

Certo, então o problema é que descartar esse arquivo não informa quando está ok para iniciar a implantação, pois os arquivos ainda estão bloqueados até que a notificação seja recebida pelo ANCM.

Além disso, o problema com app_offline.htm é que em um processo ci / cd você não tem ideia de quando o processo é interrompido.

Certo, o editor do VS usa hibernações curtas e várias tentativas.

  • Para reciclar o aplicativo, você ainda precisa adicionar e remover um arquivo appoffline. Adicionar o arquivo appoffline é uma forma de sinalizar que você deseja que o aplicativo reinicie como resultado da implantação.

Seria possível ter um arquivo "apprestart" para marcar o aplicativo a ser reciclado sem ter que excluir o arquivo appoffline posteriormente?

@Socolin por que isso?

@Socolin por que isso?

Se eu entendi bem (diga se estou errado), o que você está planejando é remover o bloqueio, mas ainda teremos que criar appoffline para sinalizar ao IIS para reciclar o aplicativo após a conclusão da compilação?

Para fazer isso, teremos que criar o arquivo appoffline e excluí-lo. Este arquivo pode ser excluído imediatamente após a criação do arquivo? ou devemos esperar que o IIS o detecte? Se for necessário esperar um pouco, eu preferiria ter um arquivo como "Apprestart" que o IIS irá escutar, e excluí-lo assim que detectá-lo e começar a reciclar.


Além disso, tenho outra pergunta em mente. É sobre a otimização do IDE durante a construção.

Atualmente trabalho em um projeto que utiliza dependências, temos um projeto para a camada Web, e outro projeto para Lógica e Dados. Quando trabalho na camada da Web (ASP.NET Core) durante a compilação, crio um arquivo appoffline antes da compilação e, em seguida, o excluo após a compilação e ele funciona.

Mas quando trabalho em uma das dependências, quando as construo do Rider (acho que está fazendo o mesmo no VS, já que está usando msbuild) quando vejo o que o msbuild está fazendo, uma vez que a construção do projeto está concluída, dll é copiado diretamente no diretório .Web / bin sem reconstruir o projeto .Web. E, como a dll está bloqueada, ela falha silenciosamente e eu tenho que reiniciar o servidor manualmente ou acionar manualmente a compilação do projeto .Web.

Você tem alguma solução para isso?
Até agora, a solução alternativa que encontrei é escrever um pequeno programa em execução em segundo plano que procura o / bin do meu projeto .Web. Quando um arquivo é alterado, ele cria um arquivo appoffline no diretório onde o IIS escuta, copia os arquivos modificados e exclui o appoffline. Mas parece um pouco hackeado, mas até agora é a única maneira que encontrei de ser capaz de construir, então pressione F5 e teste minhas alterações.

Para fazer isso, teremos que criar o arquivo appoffline e excluí-lo. Este arquivo pode ser excluído imediatamente após a criação do arquivo? ou devemos esperar que o IIS o detecte? Se for necessário esperar um pouco, eu preferiria ter um arquivo como "Apprestart" que o IIS irá escutar, e excluí-lo assim que detectá-lo e começar a reciclar.

Como o IIS saberá quando você terminar de copiar os arquivos? Como você evita o estado rasgado? Basicamente, você precisa sinalizar o início e a conclusão de uma transação. A criação do arquivo sinaliza o início e a exclusão sinaliza o fim.

Você tem alguma solução para isso?
Até agora, a solução alternativa que encontrei é escrever um pequeno programa em execução em segundo plano que procura o / bin do meu projeto .Web. Quando um arquivo é alterado, ele cria um arquivo appoffline no diretório onde o IIS escuta, copia os arquivos modificados e exclui o appoffline. Mas parece um pouco hackeado, mas até agora é a única maneira que encontrei de ser capaz de construir, então pressione F5 e teste minhas alterações.

O VS lida com isso eliminando o processo antes de iniciar um novo arquivo para qualquer coisa que possa impactar a construção daquele projeto.

Para fazer isso, teremos que criar o arquivo appoffline e excluí-lo. Este arquivo pode ser excluído imediatamente após a criação do arquivo? ou devemos esperar que o IIS o detecte? Se for necessário esperar um pouco, eu preferiria ter um arquivo como "Apprestart" que o IIS irá escutar, e excluí-lo assim que detectá-lo e começar a reciclar.

Como o IIS saberá quando você terminar de copiar os arquivos? Como você evita o estado rasgado? Basicamente, você precisa sinalizar o início e a conclusão de uma transação. A criação do arquivo sinaliza o início e a exclusão sinaliza o fim.

Se não houver bloqueio, por que notificar o IIS quando a compilação começar? Não podemos apenas sinalizar quando todos os arquivos forem atualizados e o aplicativo precisar ser reciclado? Acho que entendi mal alguma coisa.

Se não houver bloqueio, por que notificar o IIS quando a compilação começar? Não podemos apenas sinalizar quando todos os arquivos forem atualizados e o aplicativo precisar ser reciclado? Acho que entendi mal alguma coisa.

Sim, isso funcionaria. Achei que você estava descrevendo o estado da arte atual. Pode até ser possível fazer isso hoje com web.config. Se você tocar no arquivo, o aplicativo será reiniciado.

Tive sucesso com a implantação local (https://flukefan.github.io/ZipDeploy/), por:

  1. Renomear arquivos de montagem (aparentemente você pode renomear mesmo que não possa excluir / sobrescrever);
  2. Copie nas novas montagens;
  3. Toque na configuração da web para reiniciar o aplicativo;
  4. Exclua as antigas montagens renomeadas.

Sem balanceamento de carga; instância única do IIS; sem alterações no IIS. YMMV.

@FlukeFan Como você evita implantações

Eu não toco no web.config até que todos os assemblies sejam renomeados / atualizados. Acho que há uma pequena chance de que algo mais possa fazer com que um pool de aplicativos seja reciclado durante esse tempo, mas isso ainda não aconteceu.

Eu geralmente desativo todas as outras opções de reciclagem automática e também desabilito a reciclagem sobreposta nas configurações do pool de aplicativos do IIS.

@FlukeFan não é uma reciclagem, mas se você ainda não carregou o conjunto que foi substituído. Por exemplo, digamos que você tenha um assembly A.dll e B.dll. Vamos dizer que B é usado quando você vai para a página de configurações, então é carregado. Se você acessou a página de configurações quando o B.dll foi substituído pelo novo, pode acabar com o aplicativo antigo rodando com um novo B.dll.

Eu me pergunto se deveríamos fazer uma ferramenta global dotnet para auxiliar neste tipo de implantação. A ferramenta poderia basicamente fazer todas as coisas difíceis que as pessoas mencionaram neste tópico.

Pensamentos?

@FlukeFan não é uma reciclagem, mas se você ainda não carregou o conjunto que foi substituído. Por exemplo, digamos que você tenha um assembly A.dll e B.dll. Vamos dizer que B é usado quando você vai para a página de configurações, então é carregado. Se você acessou a página de configurações quando o B.dll foi substituído pelo novo, pode acabar com o aplicativo antigo rodando com um novo B.dll.

Portanto, não estou tratando desse caso agora. https://github.com/FlukeFan/ZipDeploy/blob/master/ZipDeploy/ZipDeploy.cs#L55

Acho que poderia fazer as coisas em uma ordem diferente, tocar no web.config primeiro, aguardar a conclusão de todas as 'outras' solicitações e, por fim, fazer a substituição pouco antes da última solicitação terminar (enquanto o IIS enfileira novas solicitações para o próximo reciclar app-pool), mas o que eu tenho funciona para mim agora.

Provavelmente não é robusto o suficiente para o cenário que você está descrevendo (mas eu não tenho feito nada muito sofisticado com o carregamento dinâmico de assembly, exceto no momento da inicialização).

Há planos de revisitar esse recurso e colocá-lo no roteiro? É muito inconveniente / hostil exigir que todos que implantam um site .Net Core implementem manualmente uma estratégia de slot de teste se quiserem oferecer suporte a implantações com tempo de inatividade zero.

Ter esse recurso tornaria a transição para sites .Net Core muito mais suave para muitas pessoas e permitiria uma adoção mais rápida de sites .Net Core.

Eu nunca pensei nisso. Mas você pode muito bem ter acertado em cheio. Esta pode ser uma decisão de negócios nefasta para forçar as pessoas a usar slots azuis. Se isso era possível no passado, por que demorou tanto para "consertar" agora? Não está quebrado de uma perspectiva de negócios o que isso em mente. Talvez eu seja um pouco cínico. Mas eu lentamente observei recursos azuis que costumavam ser gratuitos serem reduzidos e movidos para camadas mais caras.

Eu me pergunto se deveríamos fazer uma ferramenta global dotnet para auxiliar neste tipo de implantação. A ferramenta poderia basicamente fazer todas as coisas difíceis que as pessoas mencionaram neste tópico.

Pensamentos?

Isso deve ser executado no servidor CI, digamos? Como enviaria os binários / pacote para o servidor de destino?

Eu nunca pensei nisso. Mas você pode muito bem ter acertado em cheio. Esta pode ser uma decisão de negócios nefasta para forçar as pessoas a usar slots azuis. Se isso era possível no passado, por que demorou tanto para "consertar" agora? Não está quebrado de uma perspectiva de negócios o que isso em mente. Talvez eu seja um pouco cínico. Mas eu lentamente observei recursos azuis que costumavam ser gratuitos serem reduzidos e movidos para camadas mais caras.

As implantações locais sem coordenação são fundamentalmente não confiáveis ​​pelos motivos mencionados acima. Nunca implementaremos a cópia de sombra novamente porque esse recurso historicamente não é confiável e causou muitos problemas no ASP.NET no .NET framework. Também funciona apenas para um tipo específico de implantação, dependente da estrutura, que é totalmente compatível com o .NET Framework. O .NET core oferece suporte a arquivo único e independente, o que torna muito mais difícil o suporte. Mesmo a sugestão acima não cobre esses casos.

Ainda acreditamos que fazer implantações no local deve ser o mais atômico possível e muitos dos problemas neste problema vêm de bugs e falta de confiabilidade da detecção e reciclagem do app_offline, então passamos muito tempo consertando isso (e o bloqueio de pdb também).

Web deploy é a única ferramenta hoje que incorpora o fluxo com o qual pensamos fazer sentido implantar, mas está preso a essa tecnologia, então não ajuda se você estiver implantando ftp em seus servidores ou copiando para um compartilhamento de arquivo (embora seja poderia).

Uma ferramenta global dotnet pode ser uma resposta aqui.

Isso deve ser executado no servidor CI, digamos? Como enviaria os binários / pacote para o servidor de destino?

Acho que não. Provavelmente suportaria FTP e uma cópia direta. Qualquer coisa diferente disso é provavelmente um protocolo proprietário.

Esperançosamente, quando você diz FTP, você também quer dizer ftp sobre ssh. Essa ferramenta seria muito valiosa.

Esperançosamente, quando você diz FTP, você também quer dizer ftp sobre ssh. Essa ferramenta seria muito valiosa.

Não, eu quis dizer ftp. Mas faríamos tudo o que fizesse sentido. Talvez seja melhor ser uma ferramenta de cópia que você precisa para executar na máquina de destino. Isso nos tira do negócio de enviar bits para qualquer lugar.

Além disso, você está usando SSH em suas máquinas com Windows para implantar no IIS?

@davidfowl Adicione suporte (
https://github.com/dotnet/aspnetcore/issues/3719#issuecomment -473183712
https://github.com/dotnet/aspnetcore/issues/3793#issuecomment -335666414

Planejamos olhar para carregar assemblies de aplicativos na memória como bytes, em vez de carregá-los do disco e bloquear o arquivo. Isso deve funcionar na maioria dos casos, mas pode resultar em mais uso de memória em alguns casos .

Além disso, isso não nos permitirá atingir o tempo de inatividade zero.

Não há como investir em um recurso como esse com o módulo.

Esperançosamente, quando você diz FTP, você também quer dizer ftp sobre ssh. Essa ferramenta seria muito valiosa.

Não, eu quis dizer ftp. Mas faríamos tudo o que fizesse sentido. Talvez seja melhor ser uma ferramenta de cópia que você precisa para executar na máquina de destino. Isso nos tira do negócio de enviar bits para qualquer lugar.

Além disso, você está usando SSH em suas máquinas com Windows para implantar no IIS?

Sim. Usamos CI / CD e implantamos em sistemas Linux e Windows, então foi muito mais fácil instalar o openssh nas caixas do Windows.

$ appPool = 'default'
Stop-WebAppPool -Name $ appPool -Passthru
... implantar ...

Start-WebAppPool -Name $ appPool -Passthru
... implantar .....

ou

$ appPool = 'default'
Stop-WebAppPool -Name $ appPool
while ((Get-WebAppPoolState -Name $ appPool) .Value -ne 'Stop') {
dormir 1 #segundo
}
... implantar ...

Start-WebAppPool -Name $ appPool
while ((Get-WebAppPoolState -Name $ appPool) .Value -ne 'Iniciar') {
dormir 1 #segundo
}

... implantar .....

Slots de implantação do pobre homem
Há cerca de um ano, postei sobre a criação de 2 pastas raiz do site e tenho um link simbólico que aponta para uma delas configurada para o site IIS.
A vantagem é que seu tempo de inatividade é menor em comparação com outras soluções mais simples.
Se a reciclagem sobreposta estiver habilitada, não haverá tempo de inatividade, apenas a penalidade de aquecimento para a primeira solicitação, o que é o caso de qualquer maneira.

Se a operação de implantação / cópia levar N segundos para ser concluída, você não precisará interromper o pool de aplicativos por N segundos.
Sem mexer com o questionável app offline.
Basta copiar para a pasta inativa, trocar o link simbólico quando os arquivos estiverem lá e reciclar o pool de aplicativos.
Implementação atômica, sem tempo de inatividade, apenas a primeira solicitação mais lenta, como de costume.

https://github.com/dotnet/aspnetcore/issues/3793#issuecomment -459870870

Alguém tentou isso?
Se algo assim funcionar, pode ser transformado em um serviço com uma ferramenta de implantação do lado do cliente.

  • cliente -> servidor qual é a pasta / compartilhamento inativo?
  • S: aqui
  • C: ok implantado / copiado
  • S: link simbólico trocado e reciclado
  • C: thx, http get / healthcheck (opcional)
  • C: ugh eu errei, reversão, por favor
  • S: troca de link simbólico, reciclagem (de volta aos negócios)
  • C: enxágue repetir até que funcione

Portanto, também estou tendo problemas com arquivos bloqueados de implantação. na hospedagem de aplicativos. Tentei interromper o pool de aplicativos, mas os arquivos permanecem bloqueados. Tentei usar o app_offline.htm, mas também não tive sorte.
Além disso, se quiser gerenciar a exclusão do arquivo app_offline.htm, você pode fazer isso facilmente ao iniciar o aplicativo em program.cs. Desta forma, sabemos quando começa e fica online.

O problema atual que estou enfrentando é que as dlls estão travadas e, mesmo que você renomeie e copie os arquivos, o processo antigo ainda está em execução nos arquivos renomeados. Portanto, quando tentarmos implantar novamente, teremos que renomear os arquivos novamente. Acho que poderíamos usar algum tipo de data para substituir o nome do arquivo.
Estamos usando dotnet core 3.1.4 para isso no servidor IIS Windows 2012 R2

@foxjazz como os arquivos ainda estão bloqueados se o pool de aplicativos / processo for interrompido? Você pode elaborar?

@foxjazz como os arquivos ainda estão bloqueados se o pool de aplicativos / processo for interrompido? Você pode elaborar?

image

image

Ao tentar excluir as dll's usadas, a imagem diz usado por outro processo.
O pool de aplicativos está offline por um tempo.

Verifique o gerenciador de tarefas para ver se o processo ainda está em execução. Apertar o botão de parada não significa que termina imediatamente.

Verifique o gerenciador de tarefas para ver se o processo ainda está em execução. Apertar o botão de parada não significa que termina imediatamente.

a imagem acima é do gerenciador de tarefas. Não tenho certeza de qual é, mas se eu excluir, o arquivo será liberado. Acho que posso usar o identificador para descobrir o pid para encerrar. Não achei que teria que ir tão longe para implantação. O identificador diz que os processos w3wp.exe 2 têm um dos arquivos bloqueados. mesmo que o pool de aplicativos para ele esteja encerrado.

O que seria legal mesmo é ter uma rota para interromper o processo.
api \ KillDeathKill
{
Program.KillProcess ()
}

Isso é improvável. O arquivo está bloqueado por um processo. Se w3wp for esse processo, ele nunca foi encerrado para começar ou um novo foi acionado com um pid diferente bloqueando o mesmo arquivo. É por isso que você precisa se certificar de que o aplicativo offline esteja desativado primeiro para garantir que não haja nenhum novo processo para bloquear o arquivo, eliminar o processo existente, implantar novos bits, remover o aplicativo offline

Isso é improvável. O arquivo está bloqueado por um processo. Se w3wp for esse processo, ele nunca foi encerrado para começar ou um novo foi acionado com um pid diferente bloqueando o mesmo arquivo. É por isso que você precisa se certificar de que o aplicativo offline esteja desativado primeiro para garantir que não haja nenhum novo processo para bloquear o arquivo, eliminar o processo existente, implantar novos bits, remover o aplicativo offline

quando você diz que app_offline.htm está desativado primeiro. Sim, foi esse o caso. Tenho um caminho de status que posso verificar por meio de uma solicitação get e está definitivamente offline. Também interrompeu o pool de aplicativos manualmente. Mas o processo w3wp ainda estava travando o arquivo.

Então o processo não foi interrompido

Então o processo não foi interrompido

Então, por que interromper o pool de aplicativos, não interromper o processo?

O pool de aplicativos representa o processo e se você interromper o pool de aplicativos, ele desativará o processo. O arquivo não pode ser bloqueado se não houver processo para bloqueá-lo, então uma dessas outras teorias parece mais provável.

O pool de aplicativos representa o processo e se você interromper o pool de aplicativos, ele desativará o processo. O arquivo não pode ser bloqueado se não houver processo para bloqueá-lo, então uma dessas outras teorias parece mais provável.

hah você disse merda. Não quero parecer que estou te batendo. Estou dizendo que interromper o pool de aplicativos NÃO está funcionando conforme anunciado em toda a documentação. O processo não está desativado, mesmo depois de 90 segundos, ele ainda está em execução. O arquivo está bloqueado mesmo quando você pode ver claramente que o pool de aplicativos foi interrompido. O processo contém um singleton para obter alguns dados. E outros controladores. Seria bom ter um script que encerrasse o processo com base no arquivo bloqueado. Eu poderia então implantar sem medo. Renomear também pode funcionar. Os arquivos podem ser renomeados. sabor (dotnet core 3.1.4)

Há cerca de um ano, postei sobre a criação de 2 pastas raiz do site e tenho um link simbólico que aponta para uma delas configurada para o site IIS.

Alguém tentou isso?
Se algo assim funcionar, pode ser transformado em um serviço com uma ferramenta de implantação do lado do cliente.

@CJHarmath - Eu tentei e tenho o código modificado que estou usando abaixo. Funciona muito bem, obrigado!

Se eu publicasse mais atualizações, poderia considerar uma ferramenta de implantação de cliente, mas na verdade posso apenas fazer o shell remoto no servidor IIS e executar o script do PowerShell para invertê-lo.
psexec.exe \\IIS_SERVER cmd /c "powershell -noninteractive -file C:\FlipSymLink.ps1"

# FlipSymLink.ps1

Import-Module WebAdministration # run as admin
# create 2 site root directories
$a = 'C:\Websites\MySiteA'
$b = 'C:\Websites\MySiteB'
$appRoot  = 'C:\Websites\MySite'
$appName  = 'MyAppName'
$siteName = 'MySiteName'
$poolName = "MyPoolName"
New-Item -Type Directory $a -ErrorAction SilentlyContinue
New-Item -Type Directory $b -ErrorAction SilentlyContinue

# create a symlink to targeting one side
New-Item -Type SymbolicLink -Path $appRoot -Target $a -Name A -ErrorAction SilentlyContinue
New-Item -Type SymbolicLink -Path $appRoot -Target $b -Name B -ErrorAction SilentlyContinue

# point the site root to the symlink
$currentPath = (Get-ItemProperty "IIS:\Sites\$siteName\$appName" -name physicalPath)
if ($currentPath -eq "$appRoot\A") {
    Write-Host "Switched to B"
    New-Item $b\active.txt
    Remove-Item $a\active.txt
    Set-ItemProperty "IIS:\Sites\$siteName\$appName" -name physicalPath -value $appRoot\B
} else {
    Write-Host "Switched to A"
    New-Item $a\active.txt
    Remove-Item $b\active.txt
    Set-ItemProperty "IIS:\Sites\$siteName\$appName" -name physicalPath -value $appRoot\A
}
Restart-WebAppPool -Name $poolName

psexec.exe \\IIS_SERVER cmd /c "powershell -noninteractive -file C:\FlipSymLink.ps1"

Isso também pode ser transformado em um ponto de extremidade JEA , para que você possa executar flip como um usuário não elevado (por exemplo, de uma etapa de implantação de servidor CI / CD)
Invoke-Command -ComputerName IIS_SERVER -ConfigurationName IIS-Flip -ScriptBlock { Switch-SymlinkTarget -SiteName MySite}
Usou Switch vez de Flip porque é um verbo aprovado.

Usou Switch vez de Flip porque é um verbo aprovado.

Swap é o verbo usado ao lidar com slots - faria sentido reutilizá-lo aqui, parece?

tendo o mesmo problema descrito há 4 anos.
alguma atualização disso?
Não quero abrir a área de trabalho remota apenas para enviar um novo arquivo dll.

Quando tento sobrescrever o arquivo DLL do aplicativo .NET Core usando FTP na produção, o arquivo do servidor IIS está bloqueado e não pode ser sobrescrito.

Para implantar a nova versão, preciso interromper o aplicativo no IIS (abrindo o RDP) para liberar o bloqueio e, em seguida, substituí-lo.
Não é possível implantar sem parar o aplicativo.

Alguma solução para isso? obrigado

Se você não pode colocar seu host offline atrás do balanceador de carga e gostaria de fazê-lo mais rápido, aqui está uma maneira com links simbólicos e algum powerhell.
Eu estava pensando em fazer algo assim.

É mais rápido, pois você não precisa interromper o pool de aplicativos durante a cópia + reciclagem, é melhor do que uma parada brusca, pois permite que as solicitações atuais sejam concluídas, reduzindo o tempo de inatividade.

# Setup
Import-Module WebAdministration
# create 2 site root directories
$a = 'C:\inetpub\AspNetCoreSampleA'
$b = 'C:\inetpub\AspNetCoreSampleB'
$siteRoot = 'C:\inetpub\aspnetcoresample'
$siteName = 'AspNetCoreSample'
$poolName = "aspnetcore"
New-Item -Type Directory $a
New-Item -Type Directory $b
# create a symlink to targeting one side
New-Item -Type SymbolicLink -Path $siteRoot -Target $a
# point the site root to the symlink
Set-ItemProperty "IIS:\Sites\$siteName" -name physicalPath -value $siteRoot
# make sure it get's picked up
Restart-WebAppPool -Name $poolName

# this tells you the active side
Get-Item -Path $siteRoot | Select-Object -ExpandProperty target

# Flip the symlink
$current = (Get-Item -Path $siteRoot).Target
$newTarget = if ($current -eq $a) {$b} else {$a}
New-Item -Type SymbolicLink -Path $siteRoot -Target $newTarget -Force
# at this point w3wp.exe still locks the current target folder until it's getting recycled
# Deploy new version to the symlink which is now pointing to the other side which should have no locks
robocopy \\myshare\myapp $siteRoot /mir
# recycle app pool, so it picks up the new files
Restart-WebAppPool -Name $poolName

# bonus point: rollback is easy
$current = (Get-Item -Path $siteRoot).Target
$newTarget = if ($current -eq $a) {$b} else {$a}
New-Item -Type SymbolicLink -Path $siteRoot -Target $newTarget -Force
Restart-WebAppPool -Name $poolName

Aqui está a essência
https://gist.github.com/csharmath/b2af0f50700ce9fbdd8c5c3e582fd41b

Aqui está algo que acho que resolvi bem. Só tentei com ASP.NET, mas imagino que funcionaria da mesma forma com o módulo de hospedagem para net core. A primeira execução interrompe um pouco o site, portanto, convém ajustá-la um pouco.

param(
    [Parameter(Mandatory = $true)]
    [string] $iisRootPath,
    [Parameter(Mandatory = $true)]
    [string] $siteName,
    [Parameter(Mandatory = $true)]
    [string] $artifactPath,
    [Parameter(Mandatory = $false)]
    [string] $siteFolderName,
    [Parameter(Mandatory = $false)]
    [string] $appPoolName
)
Import-Module WebAdministration

#populate optional parameters
if (!($PSBoundParameters.ContainsKey('siteFolderName'))) { $siteFolderName = $siteName }
if (!($PSBoundParameters.ContainsKey('appPoolName'))) { $appPoolName = $siteName }

#set A, B and C paths
$a = "$iisRootPath\$siteFolderName" + 'A'
$b = "$iisRootPath\$siteFolderName" + 'B'
$c = "$iisRootPath\$siteFolderName"

#existence test
$cName = Get-Item -Path $c -ErrorAction SilentlyContinue | Select-Object -ExpandProperty name -ErrorAction SilentlyContinue
$bName = Get-Item -Path $b -ErrorAction SilentlyContinue | Select-Object -ExpandProperty name -ErrorAction SilentlyContinue

#get the shudown timeout for the app pool
$shutdownTimeout = Get-WebConfigurationProperty -Filter 'system.web/httpRuntime' -PSPath "IIS:\Sites\$siteName" -Name shutdownTimeout | Select-Object -ExpandProperty Value

#create symlink, if symlink source is an existing directory, rename existing 
if($cName -eq $null) 
{
    Write-Output "Creating SymbolicLink $c -> $a" 
    New-Item -Type SymbolicLink -Path $c -Target $a
} 
else
{
    #directories don't have a target property
    $cTarget = Get-Item -Path $c | Select-Object -ExpandProperty target -ErrorAction SilentlyContinue

    #this is a directory, rename and create symlink
    if($cTarget -eq $null)
    {
        #restart AppPool first so no locked files
        #Write-Output "Restarting AppPool $appPoolName" 
        #Restart-WebAppPool -Name $appPoolName

        #stop AppPool first so no locked files
        Write-Output "Stopping AppPool $appPoolName" 
        Stop-WebAppPool -Name $appPoolName

        #sleep for shutdownTimeout
        Write-Output "Sleeping for $shutdownTimeout" 
        Start-Sleep -Seconds (([System.TimeSpan]::Parse("$shutdownTimeout").TotalSeconds) * 1.1)

        try {
             #if the rename fails, there are files in use. stop the script
            $newName = $siteFolderName + 'A'
            Write-Output "Renaming $c to $newName" 
            Rename-Item -Path $c -NewName $newName -ErrorAction Stop
        }
        catch {
            Write-Error -Message "Failed to rename $c to $newName due to files in use."

            #start AppPool 
            Write-Output "Starting AppPool $appPoolName due to failed folder rename. Consider trying again durring a time with reduced traffic." 
            Start-WebAppPool -Name $appPoolName

        }

        #create symlink
        Write-Output "Creating SymbolicLink $c -> $a" 
        New-Item -Type SymbolicLink -Path $c -Target $a

        #start AppPool 
        Write-Output "Starting AppPool $appPoolName" 
        Start-WebAppPool -Name $appPoolName

        #copy a to b to pick up directory permissions
        if($bName -eq $null) 
        {
            Write-Output "Copying Directory $a to $b"  
            robocopy $a $b /e /sec /sl
        }
    }
}

#existence test
$aName = Get-Item -Path $a -ErrorAction SilentlyContinue | Select-Object -ExpandProperty name -ErrorAction SilentlyContinue
$bName = Get-Item -Path $b -ErrorAction SilentlyContinue | Select-Object -ExpandProperty name -ErrorAction SilentlyContinue

#create if needed
if($aName -eq $null) 
{
    Write-Output "Creating Directory $a"  
    New-Item -Type Directory $a 
}
if($bName -eq $null) 
{
    Write-Output "Creating Directory $b"  
    New-Item -Type Directory $b 
}

#make sure site pointing to symlink
Write-Output "Pointing Website $siteName to Path $c" 
Set-ItemProperty "IIS:\Sites\$siteName" -name physicalPath -value $c

#restart so we know it's loading from symlink
Write-Output "Restarting AppPool $appPoolName" 
Restart-WebAppPool -Name $appPoolName

#sleep for shutdownTimeout 
Write-Output "Sleeping for $shutdownTimeout" 
Start-Sleep -Seconds ([System.TimeSpan]::Parse("$shutdownTimeout").TotalSeconds)

#get current symlink target and set new target
Get-Item -Path $c | Select-Object -ExpandProperty target
$current = (Get-Item -Path $c).Target
$newTarget = if ($current -eq $a) {$b} else {$a}

$fileOrDirectoryName = Get-Item $artifactPath | Select-Object -ExpandProperty name

#test for .zip
if(($fileOrDirectoryName).ToLower().EndsWith(".zip") -eq $true)
{
    #copy to temp location
    $guid = [System.Guid]::NewGuid()
    $tempPath = "$iisRootPath\temp-$guid"
    $tempDest = "$iisRootPath\temp-dest-$guid"

    Write-Output "Creating temp directory $tempPath" 
    New-Item -Type Directory $tempPath

    $artifactDirectory = [System.IO.Path]::GetDirectoryName($artifactPath)
    $artifactFile = [System.IO.Path]::GetFileName($artifactPath);

    #TODO: first robocopy arg needs to be a directory, not a file
    Write-Output "Copying archive from $artifactPath to $tempPath" 
    robocopy $artifactDirectory $tempPath $artifactFile

    #extract to temp destination (seems a failed extract can leave empty folders)
    Write-Output "Extracting archive to $tempDest" 
    Expand-Archive -Path "$tempPath\$fileOrDirectoryName" -DestinationPath $tempDest -Force

    #copy from temp destination to destination
    Write-Output "Copying files from $tempDest to $newTarget" 
    robocopy $tempDest $newTarget /e

    #delete temp directory
    Write-Output "Removing temp directory $tempPath" 
    Remove-Item -Path $tempPath -Recurse -Force

    #delete temp directory
    Write-Output "Removing temp directory $tempDest" 
    Remove-Item -Path $tempDest -Recurse -Force
}
else 
{
    #copy new files
    Write-Output "Copying files from $artifactPath to $newTarget" 
    robocopy $artifactPath $newTarget /e
}

#swap symlink
Write-Output "Pointing SymbolicLink $c -> $newTarget" 
New-Item -Type SymbolicLink -Path $c -Target $newTarget -Force

#restart so it loads from newly swapped symlink
Write-Output "Restarting AppPool $appPoolName" 
Restart-WebAppPool -Name $appPoolName

@LucidObscurity o que é isso? um código cmd? onde devo adicionar esse código?
obrigado

Que tal a ideia de aumentar o número de tentativas (parâmetro: -retryAttempts:X ) ou o intervalo de tempo de repetição (parâmetro: -retryInterval:XXXX ) para o comando msdeploy , até que o processo libere o bloqueio de arquivo?

Acho que @davidfowl está certo, o processo ainda está lá de alguma forma e bloqueia os arquivos. Mesmo Reset IIS não ajuda, ele ainda espera o processo até o tempo limite. Se você descartar app_offline.htm e esperar 90 segundos (configuração de tempo limite padrão para desligar o AppPool) e tentar novamente, verá que funciona.
Para mim no local, não quero esperar tanto tempo, então atualizei este limite de tempo de desligamento (segundos) para 5 e atualizei o arquivo de projeto para gerar app_offline.htm antes de construir, removê-lo após construir. Isso funciona bem.
Para o servidor, uso msdeploy para descartar app_offline.htm, sincronizar a nova versão e remover app_offline.htm. Com msdeploy, ele tenta novamente se os arquivos estão bloqueados, mas às vezes desistia antes de 90 segundos, então eu tenho que acionar o deploy novamente, então ele funciona na segunda tentativa

Alguma atualização sobre isso? Quando esse novo lançamento estará pronto?

Obrigado por nos contatar.
Estamos movendo este problema para o marco Next sprint planning para avaliação / consideração futura. Avaliaremos a solicitação quando estivermos planejando o trabalho para a próxima etapa. Para saber mais sobre o que esperar e como esse problema será tratado, você pode ler mais sobre nosso processo de triagem aqui .

Descobri esse problema esta semana, depois de converter um aplicativo .NET 4.5.2 para .NET 5.0. Tenho outra abordagem para superar isso. Criei um serviço do Windows que está sendo executado no servidor IIS. Nesse serviço, tenho um servidor HTTP simples em execução na porta personalizada. O aplicativo da web pode fazer uma solicitação posterior a esse servidor para iniciar uma atualização. Ele para o pool de aplicativos, certificando-se de eliminar todos os processos desse aplicativo, criar backup da versão atual, remover a versão atual, baixar pacote de atualização e extraí-lo. Em seguida, inicia o app-pool novamente.

Eu criei um aplicativo Windows Forms simples, para criar pacotes de atualização que são enviados para um repo que o serviço do Windows mencionado acima obtém.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

fayezmm picture fayezmm  ·  3Comentários

mj1856 picture mj1856  ·  3Comentários

githubgitgit picture githubgitgit  ·  3Comentários

FourLeafClover picture FourLeafClover  ·  3Comentários

markrendle picture markrendle  ·  3Comentários