Verificar a versão
hostnamectlListar programas atribuídos à portas abertas
netstat -tulnpListar uso de memória RAM e CPU por processo
topVisão simplificada da memória RAM, em MB
free -mInstalar o wget
sudo yum install wgetInstalar o Apache
sudo yum install httpd mod_sslIniciar o Apache
sudo /usr/sbin/apachectl startFazer o Apache iniciar automaticamente
sudo systemctl enable httpd.serviceVerificar o status do serviço
sudo systemctl status httpdInstalar o Node.js 10 + npm
https://github.com/nodesource/distributions
yum install gcc-c++ make
curl -sL https://rpm.nodesource.com/setup_10.x | bash -
sudo yum install -y nodejsInstalar o TypeScript
sudo npm install -g typescriptInstalar o .Net Core Runtime
https://dotnet.microsoft.com/download/linux-package-manager/centos/runtime-current
sudo rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpmApesar de pedir para atualizar utilizando sudo yum update, devido ao bug que ocorreu no ambiente da última que sudo yum update foi executado no CentOS 7, resolvi não executar sudo yum update, e ainda assim o comando abaixo foi OK.
sudo yum install aspnetcore-runtime-2.2Se algum dia precisar utilizar alguma classe do assembly System.Drawing.Common, como Bitmap ou Image, pode ocorrer a exceção "The type initializer for 'Gdip' threw an exception.". Para corrigir isso, é preciso instalar a biblioteca libgdiplus:
sudo yum install libgdiplusCaso a biblioteca libgdiplus não seja encontrada, será necessário instalar o epel-release antes:
sudo yum install epel-releaseListar portas e serviços abertos
sudo firewall-cmd --list-allCaso o comando firewall-cmd não seja encontrado, será preciso instalar, iniciar e habilitar o início automático do serviço:
sudo yum install firewalld
sudo systemctl start firewalld
sudo systemctl enable firewalldLiberar a porta 80 e 443
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --reloadNesse ponto, para testar se está OK, a partir de um browser externo, basta acessar http://ip-externo (deve aparecer a página de teste do Apache).
Caminho do arquivo de configuração do Apache: /etc/httpd/conf/httpd.conf
Diretório padrão do Apache para o conteúdo dos sites: /var/www/html
Instalar o PHP 7 + Módulos PHP comuns
https://www.linuxtechi.com/install-php-7-centos-7-rhel-7-server/
sudo yum install epel-release yum-utils -y
sudo yum install http://rpms.remirepo.net/enterprise/remi-release-7.rpm
sudo yum-config-manager --enable remi-php72
sudo yum install php php-common php-opcache php-mcrypt php-cli php-gd php-curl php-mysql -y
php -v (Apenas para verificar a versão)
sudo systemctl restart httpd.service (Precisa reiniciar o Apache depois de instalar o PHP)Para verificar se o PHP está OK, pode criar o arquivo
sudo nano /var/www/html/info.phpcom o conteúdo
<?php
phpinfo();
?>e acessar http://ip-externo/info.php para confirmar se tudo está OK.
Instalar os repositórios necessários
sudo yum install epel-release mod_sslInstalar o certbot do Let's Encrypt
sudo yum install python-certbot-apacheConfigurar o certbot
sudo certbot --apache -d nome-do-subdominio.nome-do-dominio.comA configuração vai pedir um e-mail de contato e vai perguntar se deseja alterar o arquivo de configuração do servidor para redirecionar o tráfico de HTTP para HTTPS por padrão (o que é comum responder sim).
Configurar o certbot renew para executar duas vezes por dia
crontab -eO Vim será aberto para editar o crontab, então, basta acrescentar a linha
* */12 * * * /usr/bin/certbot renew >/dev/null 2>&1Para entrar no modo de edição basta teclar
Esce depois pressionar uma letra qualquer, para então poder colar a linha acima.Ao final, basta sair do modo de edição teclando
Esc, digitando:xe teclandoEnternovamente para salvar o arquivo e sair do Vim.
Baixar o rpm correto na pasta ~ (Para confirmar o link abaixo, basta entrar em https://dev.mysql.com/downloads/repo/yum/, fazer o download manual e conferir o link do arquivo RPM)
sudo wget https://repo.mysql.com/mysql80-community-release-el7-3.noarch.rpmSe quiser conferir o download, basta executar o comando abaixo e comparar o hash com o hash mostrado no site https://dev.mysql.com/downloads/repo/yum/
sudo md5sum mysql80-community-release-el7-3.noarch.rpmAtualizar o repositório
sudo rpm -ivh mysql80-community-release-el7-3.noarch.rpmPara o CentOS 8, utilizar
mysql80-community-release-el8-1.noarch.rpm.
Instalar o MySQL
sudo yum install mysql-serverIniciar o MySQL
sudo systemctl start mysqldFazer o MySQL iniciar automaticamente
sudo systemctl enable mysqldMostrar na tela a senha padrão gerada para o usuário root
sudo grep 'temporary password' /var/log/mysqld.logSe der errado, ou aparecer vazio, basta alterar a senha do usuário root manualmente
/usr/bin/mysqladmin -u root password 'nova-senha-do-root'Executar o script para terminar a instalação com segurança (quando pedir a senha atual do root, coloca a senha listada acima, depois escolhe uma senha nova para o root)
sudo mysql_secure_installationEfetuar login on MySQL com o usuário root
mysql -u root -pCriar um banco novo
CREATE DATABASE nome-do-banco;Criar um usuário novo
CREATE USER 'nome-do-usuario'@'localhost' IDENTIFIED BY 'senha-do-usuario';Conceder permissões ao usuário novo somente a esse banco de dados
GRANT ALL PRIVILEGES ON nome-do-banco.* TO 'nome-do-usuario'@'localhost';Se quiser limitar apenas para as permissões comuns
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES ON nome-do-banco.* TO 'nome-do-usuario'@'localhost';Toda vez que uma tabela/view/etc for criada pelo root, ou por outro usuário que não o usuário
nome-do-usuario, será preciso executar o comandoGRANTacima, ou ocorrerão erros na hora do usuário tentar logar/acessar!
Caso ocorra algum problema de conexão remota utilizando Node.js, .Net ou outra linguagem/framework
ALTER USER 'nome-do-usuario'@'localhost' IDENTIFIED WITH mysql_native_password BY 'senha-do-usuario';Para criar um arquivo de dump no formato .sql, basta executar o comando abaixo
mysqldump -u nome-do-usuario -p nome-do-banco > dump.sqlPara restaurar um arquivo de dump no formato .sql, basta criar o banco e o usuário utilizando os comandos acima, copiar o arquivo de dump, por exemplo dump.sql, para um diretório do servidor, ir até o diretório e executar o comando abaixo
mysql -u nome-do-usuario -p nome-do-banco < dump.sqlDependendo dos comandos utilizados dentro do arquivo de dump, antes de executar o dump será necessário executar
GRANT ALL PRIVILEGESem vez da versão apenas com permissões comuns.
Criar as pastas sites-available e sites-enabled
sudo mkdir /etc/httpd/sites-available /etc/httpd/sites-enabledAlterar o arquivo de configuração do Apache para procurar por hosts virtuais
sudo nano /etc/httpd/conf/httpd.confDepois, acrescentar essa linha ao final
IncludeOptional sites-enabled/*.confCom isso feito, basta criar o arquivo de configuração (poderia ser /etc/httpd/sites-available/site-teste.conf caso fosse uma "subpasta virtual" do domínio raiz)
sudo nano /etc/httpd/sites-available/teste.com.br.conf<VirtualHost ...>
...
</VirtualHost>O conteúdo do arquivo varia conforme o objetivo desejado/linguagem utilizada.
Por fim, basta criar um link simbólico de sites-enabled/xxx.conf apontando para o arquivo real sites-available/xxx.conf (faz isso para poder excluir o arquivo/link da pasta sites-enabled, que é onde o Apache vai procurar de verdade, mas não perder o arquivo de configuração real, que pode ser reutilizado depois)
sudo ln -s /etc/httpd/sites-available/teste.com.br.conf /etc/httpd/sites-enabled/teste.com.br.confDepois disso, deve-se reiniciar o Apache
sudo systemctl restart httpd.serviceNormalmente o SELinux vai bloquear o Apache de realizar conexões remotas, o que faz com que o Apache nunca funcione como proxy reverso no CentOS, nem que ele consiga realizar requisições para outros servidores!!! Para corrigir isso:
sudo /usr/sbin/setsebool -P httpd_can_network_connect 1
Se o Apache for acessar os arquivos por si próprio, deve-se criar as pastas do Apache para o subdomínio teste.com.br (poderia ser /var/www/site-teste/xxx caso fosse uma "subpasta virtual" do domínio raiz). As pastas convencionais são:
sudo mkdir -p /var/www/teste.com.br/html
sudo mkdir -p /var/www/teste.com.br/log
sudo mkdir -p /var/www/teste.com.br/codeExecutar as linhas abaixo apenas nos diretórios onde o usuário apache terá permissão de escrita:
sudo chown -R apache /var/www/teste.com.br/logPara garantir que as permissões estejam corretas
sudo chmod -R 755 /var/wwwRead (r) 4
Write (w) 2
Execute (x) 1
755 = owner (primeiro dígito: 7) group (segundo dígito: 5) others (terceiro dígito: 5)
Isso fará com que o usuário apache (que é o usuário padrão do Apache) consiga apenas ler e executar os arquivos das pastas. Para conferir as permissões de um arquivo, basta executar ls -l para ver todos os arquivos/diretórios do diretório atual, ou ls -l nome-do-arquivo, como ls -l app.js, para ver apenas as permissões daquele arquivo.
A saída vai ser algo como
-rw-r--r--. 1 root root 1479 Ago 12 21:48 app.js
-rw-r--r-- é dividido em 4 partes:
O primeiro caractere é o tipo (- arquivo comum, d diretório ou l link simbólico). Depois são três grupos com três caracteres com as permissões do owner, grupo e others.
-: arquivo comumrw-: owner rwr--: group rr--: others r
O primeiro root é o owner e o segundo root é o grupo ao qual aquele arquivo pertence
Antes de fazer deploy de qualquer aplicação, é preciso instalar o PM2, que é um gerenciador de processos de aplicações Node.js. Ele reinicia a aplicação Node caso ela pare de funcionar.
sudo npm install -g pm2@latestPara evitar que o PM2 seja executado com qualquer usuário, é interessante criar um usuário específico, sob o qual as aplicações serão executadas (aqui o usuário é pm2, com a home do usuário definida para /home/pm2, diretório que será criado durante o processo)
sudo useradd -d /home/pm2 -m -s /bin/bash pm2
sudo passwd pm2Fazer com que o PM2 inicie automaticamente (deve configurar corretamente o usuário e o diretório inicial, que aqui serão os mostrados acima, lembrando que o segundo pm2 no nome do serviço pm2-pm2 é na verdade o nome do usuário criado, que no caso também é pm2)
http://pm2.keymetrics.io/docs/usage/startup/
sudo pm2 startup systemd --user pm2 --hp /home/pm2
sudo systemctl enable pm2-pm2
sudo systemctl start pm2-pm2Para cada aplicação a ser adicionada, assim como ocorre com o Apache, deve-se configurar as permissões dos diretórios/arquivos que serão utilizados pelas aplicações Node gerenciadas pelo PM2
sudo chown -R pm2 /var/www/nome-do-app
sudo chmod -R 755 /var/www/nome-do-appPara iniciar uma aplicação e já adicioná-la à lista de aplicações gerenciadas (deve executar esse comando dentro do diretório onde está o arquivo app.js, e ajustar o usuário corretamente)
cd /var/www/nome-do-app
pm2 start app.js --name nome-do-app
pm2 saveCaso fosse necessário realizar um balanceamento de carga, subindo mais de uma instância da mesma aplicação, poderia ser utilizada a opção -i x com x valendo
0oumax: para criar uma instância para cada CPU do sistema-1: para criar uma instância para cada CPU do sistema, exceto uma CPUnúmero: para criar uma quantidade específica de instâncias
cd /var/www/nome-do-app
pm2 start app.js --name nome-do-app -i x
pm2 savePara reiniciar uma aplicação
pm2 restart nome-do-app --update-envPara listar aplicações Node registradas/executando
pm2 listATENÇÃO!
Os comandos
pm2 start,pm2 restartdevem ser executados pelo usuário desejado, que no caso desse exemplo é o usuáriopm2, pois as opções-u,--usere afins foram descontinuadas!O comando
pm2 savetambém deve ser executado pelo usuário desejado, caso contrário o arquivo de dump gerado será salvo no diretório errado, e não será lido corretamente durante a inicialização! O caminho do arquivo de dump é salvo é~/.pm2/dump.pm2, que no caso do exemplo é/home/pm2/.pm2/dump.pm2.Executar comandos como
pm2 start, ou até mesmopm2 list, utilizando outro usuário que não o usuário desejado, no caso do exemplo,pm2, fará com que uma nova instância do daemon do PM2 seja executada, não mostrando a situação real.Assim, para facilitar o processo todo e vitar confusão, convém efetuar o login via SSH como o usuário desejado (
pm2) e executar as tarefas relacionadas ao PM2 apenas através desse usuário!
Mais documentação sobre o PM2
http://pm2.keymetrics.io/docs/usage/quick-start/
http://pm2.keymetrics.io/docs/usage/memory-limit/
http://pm2.keymetrics.io/docs/usage/pm2-doc-single-page/
https://github.com/aspnet/AspNetCore.Docs/blob/master/aspnetcore/host-and-deploy/linux-apache.md
https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-apache?view=aspnetcore-2.1
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-2.1
https://github.com/aspnet/MetaPackages/blob/master/src/Microsoft.AspNetCore/WebHost.cs#L161
Antes de fazer deploy de qualquer aplicação, é preciso ter instalado o runtime do .Net Core, como descrito anteriormente.
Dentro de Program.cs, configurar o Kestrel (mesmo não utilizando .UseKestrel(), ele é utilizado por padrão, a não ser que outro servidor seja especificado)
return WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) => {
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
})
.UseKestrel((builderContext, options) => {
options.Configure(builderContext.Configuration.GetSection("Kestrel"));
})
.UseStartup<Startup>();Configurar as opções do Kestrel, inclusive a porta, dentro de appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
...
"Kestrel": {
"EndPoints": {
"Http": {
...
"Url": "http://localhost:5003"
}
}
}
}Essa porta não afeta o comportamento durante o debug pelo IIS Express.
Em seguida, alterar o arquivo Startup.cs:
Caso a aplicação não esteja sendo executada a partir da raiz, deve-se especificar qual é a raiz, para fazer funcionar os links, controllers e o mapeamento de "~/..." do Razor:
app.UsePathBase("/teste");Com isso, convém deixar o arquivo de configuração do Apache assim:
ProxyPass /teste http://127.0.0.1:5005/teste retry=0
ProxyPassReverse /teste http://127.0.0.1:5003/testeConfigurar o app para usar encaminhamento de cabeçalhos (que será necessário quando utilizar o app .Net Core via proxy do Apache no Linux)
app.UseForwardedHeaders(new ForwardedHeadersOptions {
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});Fazer o deploy via Publish, utilizando
Deployment Mode: Framework Dependent
Target Runtime: linux-x64
Copiar os arquivos publicados para uma pasta dentro do local padrão do Apache: /var/www/nome-do-app
Configurar um serviço no Linux para iniciar/reiniciar o app automaticamente
sudo nano /etc/systemd/system/kestrel-nome-do-app.serviceConteúdo:
[Unit]
Description=nome-do-app app
[Service]
WorkingDirectory=/var/www/nome-do-app
ExecStart=/usr/bin/dotnet /var/www/nome-do-app/nome-do-app.dll
Restart=always
# Reiniciar depois de 10 segundos em caso de problemas
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=dotnet-nome-do-app
# Configurar o user correto
User=apache
Environment=ASPNETCORE_ENVIRONMENT=Production
[Install]
WantedBy=multi-user.targetConfigurar o serviço para iniciar automaticamente
sudo systemctl enable kestrel-nome-do-app.serviceIniciar o serviço
sudo systemctl start kestrel-nome-do-app.serviceVerificar o status do serviço/app funcionando (utilizar a porta correta)
sudo systemctl status kestrel-nome-do-app.service
curl localhost:5003Por fim, configurar um proxy no Apache, apontando para localhost:5003
sudo nano /etc/httpd/sites-available/nome-do-app.conf<VirtualHost ...>
...
</VirtualHost>sudo ln -s /etc/httpd/sites-available/nome-do-app.conf /etc/httpd/sites-enabled/nome-do-app.confDepois disso, deve-se reiniciar o Apache
sudo systemctl restart httpd.servicePasta virtual redirecionando para o Node
<VirtualHost *:80>
ProxyPreserveHost On
# Cuidado para não colocar o destino como http://127.0.0.1:3000/
# porque o Apache vai concatenar tudo depois da origem ao final
# do destino. Por exemplo, se vier /teste/ o Apache vai chamar
# http://127.0.0.1:3000/. Se o destino já fosse http://127.0.0.1:3000/
# então o Apache chamaria http://127.0.0.1:3000// o que pode dar
# problemas com Node e com .Net Core!!!
# Mais um exemplo: se a URL pedida for /teste/a/b/c/, como a origem
# está configurada como /teste, /a/b/c/ será concatenado ao final do
# destino, que se for http://127.0.0.1:3000, fará com que o Apache
# chame http://127.0.0.1:3000/a/b/c/
ProxyPass /teste http://127.0.0.1:3000
ProxyPassReverse /teste http://127.0.0.1:3000
</VirtualHost>Sub-domínio redirecionando para o Node
<VirtualHost teste.com.br:80>
ProxyPreserveHost On
# Se fosse o caso da raiz, aí deve-se colocar a barra ao final!
# A regra é: se a string à esquerda termina com uma barra, a da
# direita deve terminar com uma barra, também; e se a da esquerda
# não termina com uma barra, a da direita também não deve terminar
# com uma barra!
ProxyPass / http://127.0.0.1:3000/ retry=0
ProxyPassReverse / http://127.0.0.1:3000/
ServerName teste.com.br
</VirtualHost>Pasta virtual redirecionando para o Node, mas servindo arquivos estáticos direto pelo Apache, sem o Node
<VirtualHost *:80>
ProxyPreserveHost On
# Habilitando compressão conforme o tipo da resposta (vai valer
# tanto para os arquivos estáticos, como para as respostas geradas
# dinamicamente pelo Node.js)
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE image/x-icon
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/xml
# Definindo cabeçalhos específicos para um diretório
<Directory /var/www/teste.com.br/html>
Header Set Cache-Control "public,max-age=31536000"
Header Unset ETag
FileETag None
</Directory>
Alias "/teste/public" "/var/www/teste.com.br/html"
# A ordem dos ProxyPass é importante!!!
# ProxyPass /teste/public ! fará com que tudo que começar por
# /teste/public não seja encaminhado para o proxy
# https://stackoverflow.com/a/35928307/3569421
ProxyPass /teste/public !
ProxyPass /teste http://127.0.0.1:3000 retry=0
ProxyPassReverse /teste http://127.0.0.1:3000
</VirtualHost>Domínio principal (único domínio)
<VirtualHost *:80>
ServerName teste.com.br
ServerAlias www.teste.com.br
DocumentRoot /var/www/teste.com.br/public_html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>Caso tenha instalado o SSL
<VirtualHost *:80>
... Conteúdo original do arquivo ...
RewriteEngine on
RewriteCond %{SERVER_NAME} =nome-do-subdominio.nome-do-dominio.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
<IfModule mod_ssl.c>
<VirtualHost *:443>
... Conteúdo original do arquivo, igual ao mostrado acima ...
ServerName nome-do-subdominio.nome-do-dominio.com
SSLCertificateFile /etc/letsencrypt/live/nome-do-subdominio.nome-do-dominio.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/nome-do-subdominio.nome-do-dominio.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateChainFile /etc/letsencrypt/live/nome-do-subdominio.nome-do-dominio.com/chain.pem
</VirtualHost>
</IfModule>