Database connection refused with Symfony / PHP

2019-07-12 03:13发布

问题:

Trying to deploy a Symfony 3.2/Doctrine application to Swisscom PaaS.

Buildpack (PHP 7, httpd etc.) are installed, composer is running and installing dependencies, but when calling the composer after-commands, like cache:clear I get an:

[Doctrine\DBAL\Exception\ConnectionException]                              
An exception occured in driver: SQLSTATE[HY000] [2002] Connection refused

my manifest.yml:

applications:
- services:
 - dbservice
buildpack: php_buildpack
host: myapp
name: MyApp
instances: 1
memory: 640M
env:
 SYMFONY_ENV: prod
 PHP_INI_SCAN_DIR: .bp-config/php/conf.d

my options.json:

"WEB_SERVER": "httpd",
"COMPOSER_INSTALL_OPTIONS": ["--no-dev --optimize-autoloader --no-progress --no-interaction"],
"COMPOSER_VENDOR_DIR": "vendor",
"SYMFONY_ENV": "prod",
"WEBDIR": "web",
"PHP_MODULES": "fpm",
"PHP_VERSION": "{PHP_70_LATEST}",
"PHP_EXTENSIONS": [
 "bz2",
 "zlib",
 "curl",
 "mcrypt",
 "openssl",
 "mbstring",
 "pdo",
 "pdo_mysql"
],
"ZEND_EXTENSIONS": [
 "opcache"
]

And this is how I read database credentials from VCAP and set parameters in Symfony (which is perfectly working with a local setup of VCAPSERVICES env vars):

$vcapServices = json_decode($_ENV['VCAP_SERVICES']);

$container->setParameter('database_driver', 'pdo_mysql');

$db = $vcapServices->{'mariadb'}[0]->credentials;

$container->setParameter('database_host', $db->host);
$container->setParameter('database_port', $db->port);
$container->setParameter('database_name', $db->name);
$container->setParameter('database_user', $db->username);
$container->setParameter('database_password', $db->password);

// Just for debug:

echo 'User: ';
var_dump($container->getParameter('database_user'));

echo 'Db: ';
var_dump($db);

Service is running, both var_dump deliver the expected values. But still connection is refused.

What am I doing wrong?

****EDIT**** A similar problem seems to be here, but without a solution: Cloud foundry p-mysql

****EDIT****

I debugged down right to the statement where PDO constructor is called.

It is called with the following parameters:

$dsn = mysql:host=10.0.20.18;port=3306;dbname=CF_DB922DD3_CACB_4344_9948_746E585732B5;
$username = "myrealusername"; // as looked up in VCAP_SERVICES
$password = "myrealpassword"; // as looked up in VCAP_SERVICES
$options = array();

Anything looks exactly like it can be seen in the web console for the service binding.

Is an unix_socket required to successfully connect?

****EDIT****

As Symfony is using some composer post-install-commands (in this case e.g. to clear and warm up the cache) which require already a working database connection that this is not supported with DB services by cloudfoundry as long as the container is not fully built and deployed?

Am running out of ideas.

回答1:

Sorry for the issue and thanks for feedback. Swisscom modified the security groups. See Application Security Groups for more info.

Cloud Foundry blocks all outbound network connections from application containers by default. Administrators can override this block-by-default behavior with Application Security Groups (ASGs).

ASGs are a collection of egress rules that specify one or more individual protocols, ports, and destinations to allow network access to.

Cloud Foundry has two default sets of ASGs: default-staging and default-running. All application containers in Cloud Foundry use a base policy from one of these.

The rule for Galera as a Service (MariaDB) was only done in running. We added the rule also to staging.



回答2:

Just in case someone has a similar issue this was the solution:

In the end I could only solve this with PaaS provider (swisscomdev) support.

Obviously database connection was not provided/possible during the staging/deployment of our app, but Symfony's cache:clear/warmup required a full database connection during the composer post processing phase.

After a fix in the cloudfoundry based platform of swisscomdev everything worked as expected.



回答3:

In my experience, Connection refused can mean one of the things:

  • The firewall (iptables?) is blocking the non-local access to the port
  • The mysql server is not even listening to the port
  • The mysql server is listening on ipv6 port 3306 while client tries to connect via ipv4.

First let's try to narrow it down by running basic telnet test:

telnet 10.0.20.18 3306

This should greet you with some half-garbled message with clear mention of MySQL. If it does not, than you need to go back to more general restrictions like firewalls and policies.

Since you are positive that server is running, I suggest checking if you are being blocked by firewall or SELinux. Don't know how much control do you have over the Swisscom's PaaS system, though.

If you have SSH access to the PaaS service, you could try running tcpdump do capture any traffic. See this article: https://serverfault.com/questions/507627/debugging-a-connection-refused-response-on-port-21

Hope this gives you some hint...