I wrote an application in Typescript which use Typeorm to interact with a MySQL database. When I run the app using npm run dev which is a package.json script, the connection is working fine with these credentials:
"type": "mysql",
"host": "127.0.0.1",
"port": 3306,
"username": "root",
"password": "root",
"database": "mydb",
but when I launch my Docker container I get:
> Error: connect ECONNREFUSED 127.0.0.1:3306
> errno: -111,
> code: 'ECONNREFUSED',
> syscall: 'connect',
> address: '127.0.0.1',
> port: 3306,
> fatal: true
I cannot figure out of this problem, I also tried to change 127.0.0.1 with localhost but same problem. That's weird 'cause I'm using Dbeaver to connect with my LAMP which is a Docker container container too and I can establish the connection.
Seems a problem related only with the containers connection, maybe the container of the app doesn't know the network 127.0.0.1?
This is my image file:
FROM node:stretch-slim
WORKDIR /myapp
COPY package.json ./
COPY ./dist ./dist
RUN npm install
EXPOSE 4000
ENV NODE_ENV development
ENV PORT 4000
ENV URL http://localhost:4000
ENV JWT_SECRET secret
CMD ["npm", "run", "start", "dev"]
and this is my docker-compose.yml:
version: '3'
services:
app:
container_name: myapp
restart: always
build: .
ports:
- '4000:4000'
for compile my containers I did: docker-compose up --build
What is wrong? As you can see from my lamp container each port is correctly exposed:
The problem here is that each container use their own network, even though I exposed the mysql container on port 3306 I cannot access to mysql from the myapp container, because myapp container use another network.
So executing the following command: docker network ls I was able to list all the networks available, and then I did:
docker network inspect bridge
which has returned the following gateway: 172.17.0.1
The solution would be to replace localhost with 172.17.0.1. But I don't like the following solution too much, so I rebuilded the myapp image adding previously network_mode inside the docker-compose.yml, so now I have:
version: '3'
services:
app:
container_name: myapp
restart: always
build: .
ports:
- '4000:4000'
network_mode: 'host'
as documentation says:
If you use the host network mode for a container, that container’s network stack is not isolated from the Docker host (the container shares the host’s networking namespace), and the container does not get its own IP-address allocated. For instance, if you run a container which binds to port 80 and you use host networking, the container’s application is available on port 80 on the host’s IP address.
I don't know if there is a better solution out there, I'm a Docker noob so far, so maybe someone more expert on this could propose a better solution to share a container network such as mysql, and then access to that shared network from others containers.
Another solution would be "host": "host.docker.internal"
docker-compose
docker-compose isn't limited to just one service, you can have the whole system, which, in your case, means the DB server, your app, and all the other things.
You can change your docker-compose.yml file to something like below (MySQL bit copied from https://hub.docker.com/_/mysql )
version: '3'
services:
db:
image: mysql
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
MYSQL_ROOT_PASSWORD: example
app:
container_name: myapp
restart: always
build: .
ports:
- '4000:4000'
Networking
Because your containers are brought up in the same docker-compose, by default they have access to each other by hostname e.g. app, and db. To try this out, bring them up, docker attach/exec to app, and try a ping db.
See https://stackoverflow.com/a/30173220/1148483
This means that in your app, you can use db as the DB host config.
Instead of using localhost, let's replace it with host.docker.internal. This will allow you to connect with your local db
Related
I have a Dockerized django application I am running and I am trying to connect it to a mysql server I have that is port forwarded from another docker container. I have done a sanity test already and confirmed that I can connect to my mysql server using mysql workbench on my localhost.
I have my dockerized django application running on network_mode: host so I thought I would be able to simply connect. Sadly I currently error out on docker-compose build with the error django.db.utils.OperationalError: (2002, "Can't connect to MySQL server on '127.0.0.1' (115)")
An accepted resolution to this issue means that my dockerized django application would be able to connect successfully to my mysql server running localhost:29998
SETTINGS.PY (Django Application)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mytestdb',
'USER': 'userone',
'PASSWORD': 'password',
'HOST': '127.0.0.1',
'PORT': '29998',
}
}
DJANGO App compose file
version: '3.3'
services:
mydjangoapp:
container_name: mydjangoapp
restart: always
env_file: .env
build: .
volumes:
- ./apps:/apps
- ./core:/core
network_mode: host
Django app dockerfile:
FROM python:3.9
COPY . .
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
COPY requirements.txt .
# install python dependencies
RUN pip install --upgrade pip
RUN pip install --no-cache-dir -r requirements.txt
# running migrations
RUN python manage.py migrate
# gunicorn
CMD ["gunicorn", "--config", "gunicorn-cfg.py", "core.wsgi"]
Dockerized mysql server (port forwarded to localhost)
version: '3.3'
services:
mysql:
image: mysql:latest
container_name: mymysqlserver
environment:
- MYSQL_DATABASE=mytestdb
- MYSQL_USER=userone
- MYSQL_ROOT_PASSWORD=password
- MYSQL_PASSWORD=password
ports:
- 29998:3306
Do I need to create some sort of docker network / bridge for this to work? (never tried that before).
I have already attempted the following solutions: sol1 (network_mode=host), sol2,
You django app is not working because:
You are running the containers in separated docker-compose files, this causes django container runs in different network than mysql container.
You are trying to connect to localhost (127.0.0.1) inside the django container. This localhost is different to 'localhost' of your computer and is different to the 'localhost' of mysql container. There are 3 different networks. If you want to connect django container with mysql container use the same network (docker network or your computer IP assigned by a router also will works).
You are trying to connect to the exposed port 29998, but this port is exposed from mysql container to your computer. If you are trying to make an internal connection you should use 3306. (If you are using an internal connection, then you don't need to expose the port)
Why not put the two services in same docker compose file and run it from there, like this:
version: '3.3'
services:
mydjangoapp:
container_name: mydjangoapp
restart: always
env_file: .env
build: .
volumes:
- ./apps:/apps
- ./core:/core
network_mode: host
depends_on: mysql
mysql:
image: mysql:latest
container_name: mymysqlserver
environment:
- MYSQL_DATABASE=mytestdb
- MYSQL_USER=userone
- MYSQL_ROOT_PASSWORD=password
- MYSQL_PASSWORD=password
ports:
- 29998:3306
And then update the settings like this:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mytestdb',
'USER': 'userone',
'PASSWORD': 'password',
'HOST': 'mysql',
'PORT': '3306',
}
}
In that way, the communication between Django and MySQL will be done through docker network, rather than accessing the host machine network.
Apart from that, you need to change the Dockerfile, so that the migration runs after the MySQL server is running. To ensure that, you can add the migration command in the CMD part:
CMD ["sh", "-c", "python manage.py migrate;gunicorn --config gunicorn-cfg.py core.wsgi"]
I have local MySQL database created in docker container from following docker-compose.yml
version: '3.6'
services:
mysql:
environment:
- MYSQL_DATABASE=test
- MYSQL_ROOT_PASSWORD=changeme
- MYSQL_USER=dbuser
- MYSQL_PASSWORD=changeme
command:
- --table_definition_cache=100
- --performance_schema=0
- --default-authentication-plugin=mysql_native_password
- --innodb_use_native_aio=0
volumes:
- ./init:/docker-entrypoint-initdb.d
container_name: mysqldb
image: mysql
And it's working fine - I can enter docker container, log in as root user (or dbuser) and create databases, tables, execute queries, etc. I also have Nest.js application which uses #nestjs/typeorm module to connect to database using following ormconfig.json:
{
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "dbuser",
"password": "changeme",
"database": "test",
"synchronize": true,
"entiries": ["src/**/*/entity.ts"]
}
And here is the problem - when I start application I get following error:
Error: ER_ACCESS_DENIED_ERROR: Access denied for user 'dbuser'#'172.22.0.1' (using password: YES)
It seems that all data in ormconfig.json are correct - why my application can't connect to database?
You do not have the port bound to your localhost.
Quick and dirty solution:
Add the following to your mysql service in the docker-compose.yml:
ports:
- "3306:3306"
More complete solution
Move your NestJS app into your docker-compose.yml too.
Create dev.Dockerfile in your folder with something like this inside:
FROM node:16-alpine
WORKDIR /srv/app
CMD npm run start:dev
Add your app to docker-compose.yml:
api:
build:
context: .
dockerfile: ./dev.Dockerfile
volumes:
- ./:/srv/app
depends_on:
- mysql
environment:
MYSQL_HOST: "mysql"
ports:
- "3000:3000"
If you go this way, your app will access MySQL instance through the Docker Compose network, without the need to bind MySQL port to your localhost.
It will still give nice DX as you will have hot reloading in this setup.
I hope it helps. In case of issues, feel free to comment and I will explain/edit.
I have the following docker-compose.yml
version: '3'
services:
app:
build: .
network_mode: host
volumes:
- .:/usr/usr/src/app
db:
image: mysql/mysql-server:5.7
environment:
MYSQL_DATABASE: config_dev
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
volumes:
- ./docker/images/config-dev-image/sql-scripts:/docker-entrypoint-initdb.d
restart: always
ports:
- "1200:3306"
My app service needs to connect to the db and using the documentation I tried to connect to using the service-name 'db' like so (from the app container)
mysql --hostname=db --port=3306 --user=root However, I get the error ERROR 2005 (HY000): Unknown MySQL server host 'db'
What am I doing wrong?
Your app container is running with network_mode: host. If it's using the host network then it can't use any of the Docker-specific network features; for example, it can't reach other containers by host name and it can't be reached by host name. For Docker networking purposes it's indistinguishable from a process running on the host.
Host networking isn't actually necessary for most of the cases I see suggested on SO, and you should see if your application works if you just remove that line. You might need to add ports: to make it accessible from outside.
If you really can't disable host networking, then you need to connect to the database the same way other processes running outside Docker network space would, via the other container's published ports. A host name of localhost should work (because you're in the context of the host) but you need the mapped port number --port=1200.
Add links configuration in app section
version: '3'
services:
app:
build: .
network_mode: host
volumes:
- .:/usr/usr/src/app
links:
- db
db:
image: mysql/mysql-server:5.7
environment:
MYSQL_DATABASE: config_dev
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
volumes:
- ./docker/images/config-dev-image/sql-scripts:/docker-entrypoint-initdb.d
restart: always
ports:
- "1200:3306"
Because of network_mode: host your app container is effectively on a docker's private network like 192.168.65.1/24 while you db container is on a different private network created by docker-compose like 172.20.0.2/16. You can see this network being deleted when you run docker-compose down:
Removing network XXXX_default
where XXXX is your directory name.
If you were to remove network_mode: host from service app, both containers would be on the same private network and reachable by their service name.
$ docker inspect XXXX_default
"Containers": {
...
"Name": "app",
"IPv4Address": "172.21.0.3/16",
...
},
...
"Name": "db",
"IPv4Address": "172.21.0.2/16",
...
}
},
app container can access db on port 3306. No need to expose the port as 1200. As per docs:
Containers connected to the same user-defined bridge network
automatically expose all ports to each other, and no ports to the
outside world. This allows containerized applications to communicate
with each other easily, without accidentally opening access to the
outside world.
When I try to connect two docker containers on the same machine, one running a node.js server and the other running mysql dbms
I get the following error:
(node:32) UnhandledPromiseRejectionWarning: Error: getaddrinfo ENOTFOUND jdbc:mysql://localhost:3306 jdbc:mysql://localhost:3306:3306
mysql driver connection config
const connection= mysql.createConnection({
host: 'jdbc:mysql://topsectiondb:3306',
user: 'root',
password: 'rootpass'
})
docker-compose.yml
version: '3'
services:
topsection:
container_name: topsection-server
restart: always
build: .
ports:
- '7777:7777'
depends_on:
- topsectiondb
environment:
- PORT=7777
topsectiondb:
container_name: topsectiondb
image: mysql:8.0.3
ports:
- '3306:3306'
environment:
- MYSQL_ROOT_PASSWORD=rootpass
Dockerfile
FROM node:10
RUN mkdir serviceFolder
WORKDIR /usr/app/
COPY . .
RUN npm install
EXPOSE 7777
CMD ["npm", "start"]
for a more complete stack trace
https://gist.github.com/armouti/877a8b4405330c44e4009ebae3df822c
First of all there are a couple of problems here.
Firstly you need to look at networking your docker containers in your docker-compose file together. Basically each container doesn't know of the other until you specify which network they are in. Each container can be in multiple but if you want two containers to connect they need to be in the same network. https://docs.docker.com/compose/networking/#configure-the-default-network
Then Secondly you can't use localhost as a url. Basically a docker container is an isolated environment so it has it's own "localhost" so when you use that address it's actually trying to connect to the mysql on the nodejs container. So when networked properly you will be able to connect to the mysql container using their names you gave them in the docker-compose something like topsectiondb:3306 or something like this. This tells docker your using networking to connect one docker container to another and will allow you to make the initial connection. Connect to another container using Docker compose
===================================================
Actual Answer:
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'me',
password : 'secret',
database : 'my_db'
});
Basically the mysql library requires a hostname, which in most cases is localhost, but as your linking containers with docker-compose you use the alias of the container. The port number is 3306 by default
So:
host: "topsectiondb" will suffice!
When I try to tunnel via SSH to the Host Mashine (vServer) and then try to connect via the internal docker Container-IP then I can't connect to MySQL.
This is my docker-compose file.
version: '2'
services:
mysql:
build: ./mysql
environment:
MYSQL_ROOT_PASSWORD: test
volumes:
- ./db:/var/lib/mysql
The only solution I found was to forward the MySQL-Port of the mysql container to the Host-Mashine.
version: '2'
services:
mysql:
build: ./mysql
environment:
MYSQL_ROOT_PASSWORD: test
volumes:
- ./db:/var/lib/mysql
ports:
- 3306:3306
Then I am able to connect via the Host IP to MySQL but this is without SSH its direct via TCP and the port.
This is a No-Go for me to bring the MySQL Service into the internet.
Reasons can be found here https://security.stackexchange.com/questions/63881/is-it-not-safe-to-open-mysqls-port-to-the-internet why it is not a good practice to bring your mysql port into the internet.
So what is a good practice to connect to my docker mysql container with SSH but keep the mysql ports closed?
One simple way is to bind the MySQL port only to the localhost address. That assumes the host has a mysql client available outside of Docker.
ports:
- 127.0.0.1:3306:3306
You could also omit the ports section completely (no port binding at all), and use the mysql client that's already inside the container.
docker-compose exec mysql bash
Then run the mysql command inside the container to do whatever queries you want to do.
An easy way to achieve this is to forward the ssh port of the docker conatiner to some port on your host, i.e.
ports:
- 22:<some free host port>
and then access the container via ssh to the host port you used. Note, that it is a bad idea to use port 22, since that will cause a conflict with the ssh server running on your host.