Use a docker sdk to send commands to the docker machine from a web app - node.js

I'm new to Docker and I have some difficulties to understand how I should use it.
For now, I'm wondering if that makes sense to attempt sending commands to a docker machine on my computer from the client side script of a javascript web app using an SDK like Dockerode.
I installed Docker CE for windows (17.06.0-ce) and Docker Toolbox, and I ran a container on the default machine using the docker terminal. Now I'm wondering if the commands I typed could be sent from a web app using NodeJS. I tried using this code:
import Docker from 'dockerode';
const docker = new Docker({host: 'myDefaultMachineHost'});
export function createLocalDb () {
docker.pull('someImageFromDockerHub', function (err, stream) {
if (err) console.log("Catch : " + err.toString());
stream.pipe(process.stdout, {end: true});
stream.on('end', function() {
//run the container
}).catch(function (err) {
console.log("Catch : " + err.toString());
});
});
}
But that doesn't work(stream.pipe throws an error). Am I misunderstanding the context in which I'm supposed to use dockerode ?
Thanks for your explanations !

In short: You need change your code to this const docker = new Docker({socketPath: '/var/run/docker.sock'}); and add docker socket inside your container.
Theory:
You have docker socket inside your local machine. You should add this socket inside your docker container. The volume is your solution.
Image for visualization this issue:
Implementation with arguments
This is simple task for Linux/Mac user. They can do
docker run -v /var/run/docker.sock:/var/run/docker.sock ...
On Windows you need run
docker run -v //var/run/docker.sock:/var/run/docker.sock ...
More details in this question.
Implementation with Dockerfile
Also, you can add to your Dockerfile VOLUME instruction.
On Linux/Mac it should be line like this:
VOLUME /var/run/docker.sock /var/run/docker.sock
I don't know who it will be on Windows, I use Mac.

Related

How can I execute a command inside a docker from a node app?

I have a node app running, and I need to access a command that lives in an alpine docker image.
Do I have to use exec inside of javascript?
How can I install latex on an alpine container and use it from a node app?
I pulled an alpine docker image, started it and installed latex.
Now I have a docker container running on my host. I want to access this latex compiler from inside my node app (dockerized or not) and be able to compile *.tex files into *.pdf
If I sh into the alpine image I can compile '.tex into *.pdf just fine, but how can I access this software from outside the container e.g. a node app?
If you just want to run the LaTeX engine over files that you have in your local container filesystem, you should install it directly in your image and run it as an ordinary subprocess.
For example, this Javascript code will run in any environment that has LaTeX installed locally, Docker or otherwise:
const { execFileSync } = require('node:child_process');
const { mkdtemp, open } = require('node:fs/promises');
const tmpdir = await mkdtemp('/tmp/latex-');
let input;
try {
input = await open(tmpdir + '/input.tex', 'w');
await input.write('\\begin{document}\n...\n\\end{document}\n');
} finally {
input?.close();
}
execFileSync('pdflatex', ['input'], { cwd: tmpdir, stdio: 'inherit' });
// produces tmpdir + '/input.pdf'
In a Docker context, you'd have to make sure LaTeX is installed in the same image as your Node application. You mention using an Alpine-based LaTeX setup, so you could
FROM node:lts-alpine
RUN apk add texlive-full # or maybe a smaller subset
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY ./ ./
CMD ["node", "main.js"]
You should not try to directly run commands in other Docker containers. There are several aspects of this that are tricky, including security concerns and managing the input and output files. If it's possible to directly invoke a command in a new or existing container, it's also very straightforward to use that permission to compromise the entire host.

Node.js HTTP Get stream freezes inside docker container

I have following code written in nodejs using http module.
It's basically listens to event stream (so the connection is permanent).
http.get(EVENT_STREAM_ADDRESS, res => {
res.on('data', (buf) => {
const str = Buffer.from(buf).toString();
console.log(str);
});
res.on('error', err => console.log("err: ", err));
});
If I run the above code on my Mac it works fine and I get data logged to console after many hours.
But in the Docker, which has pretty basic configuration it stops receiving data after some time without any error. Other endpoints in the app are working fine (eg. Express endpoints) but this one http.get listener is just hanging.
Dockerfile
FROM node:current-alpine
WORKDIR /app
EXPOSE 4000
CMD npm install && npm run start
Do you have any ideas how I can overcome this?
It's really hard to debug as to reproduce the situation I sometimes need to wait a few hours.
Cheers
I find out what is probably happening.
The problem is Docker Desktop for Mac. It seems it stops container sometimes like in the situation that you Mac goes to sleep. And it interrupts the listener so it stops receiving new data chunks.
I started same container on Ubuntu in VirtualBox and it works fine all the time.

Is it possible to run Selenium like tests in a Windows Docker Container

we have a Windows Electron application that runs e2e Tests via Spectron. The application is platform-dependent and won't run on Linux (Containers). We want to run our Spectron e2e Tests inside a preconfigured Docker container to have them isolated.
To get a grasp of it I have built a minimal nodejs application that does basically nothing and has an e2e test (jest) that opens a browser tab and checks the title, no functionality just a simple spike.
I created a Dockerfile to build a container to run the tests:
FROM mcr.microsoft.com/windows:20H2-amd64
RUN mkdir "C:/app"
WORKDIR "C:/app"
COPY app "C:/app"
RUN powershell -Command \
Set-ExecutionPolicy unrestricted;
ENV chocolateyUseWindowsCompression false
RUN powershell -Command \
iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'));
RUN choco install googlechrome -y --version=91.0.4472.101 --ignore-checksums
RUN choco install chromedriver -y --version=91.0.4472.1010 --ignore-checksums
RUN choco install nodejs-lts -y --version=14.17.1
RUN npm config set strict-ssl false
RUN npm install
ENTRYPOINT npm test
Note this is a Windows container, as our main app will also need a Windows container to run. The container builds and runs the test but crashes with the error: SessionNotCreatedError: session not created thrown by from tab crashed. On my Windows Host, the test runs fine.
Is there anything wrong with my Dockerfile or is this simply not possible in a Windows container?
I don't think it's relevant to the problem but here is also the test file that gets executed when the container does npm test:
describe('test google.com', () => {
const {
Builder,
By,
Key,
until
} = require('selenium-webdriver');
var driver;
beforeEach(() => {
driver = new Builder()
.forBrowser('chrome')
.build();
});
afterEach(() => {
driver.quit();
});
it('should open google search', async () => {
await driver.get('http://www.google.com');
driver
.getTitle()
.then(title => {
expect(title).toEqual('Google');
});
});
});
We had a similar problem, but we are using .net-core with Selenium. For some reason, installing the Chromedriver did not work inside container, so we had to do two things:
manually download the driver based on the chrome version and export the zip into the working directory. (It's been a while though, and we did not really update the image, installing via choco may be working now)
Even stranger thing is that we had to install some fonts for some reason.
Take look at my repo: https://github.com/yamac-kurtulus/Windows-Docker-Images/tree/master/DotnetCore%20Selenium%20With%20Chrome
The relevant part is after line 23 in the Dockerfile.
Note: If you are not very deep into the project, I strongly suggest you to migrate to Linux. Working with Docker on Windows is like a nightmare that you cannot wake up from.

Connecting to MongoDB in Docker from external app

Is it possible to connect to a docker container running a MongoDB image from an external nodejs application running locally? I've tried connecting via localhost:27017. Here's the docker compose file I'm using:
version: '3'
services:
mongodb:
image: 'bitnami/mongodb:3.6.8'
ports:
- "27017:27017"
environment:
- MONGODB_ROOT_PASSWORD=$MONGODB_ROOT_PASSWORD
- MONGODB_USERNAME=$MONGODB_USERNAME
- MONGODB_PASSWORD=$MONGODB_PASSWORD
- MONGODB_DATABASE=$MONGODB_DATABASE
volumes:
- /data/db:/bitnami
I try connecting to it with the following url with no luck:
mongodb://${process.env.MONGODB_USERNAME}:${process.env.MONGODB_PASSWORD}#localhost:27017
EDIT: Connecting via mongodb://localhost:27017 works, but the authentication url errors out. I printed out the result of this string and there's nothing particularly wrong with it. I verified that the username and password match the users inside mongo in the docker container.
app.listen(port, () => {
console.log(`Example app listening on port ${port}!`);
const url = (() => {
if(process.env.MONGODB_USERNAME && process.env.MONGODB_PASSWORD) {
return `mongodb://${process.env.MONGODB_USERNAME}:${process.env.MONGODB_PASSWORD}#localhost:27017/`;
}
console.log('could not find environment vars for mongodb');
})();
MongoClient.connect(url, (err, client) => {
if(err) {
console.log('DB connection error');
} else {
console.log("Connected successfully to server");
client.close();
}
});
});
If the external nodejs application is also running in a docker container then you need to link the containers. Here is an example of a docker run cmd that links containers. I added environment variables to illustrate what host name and port you would use from inside the container.
docker run -d -it -e DEST_PORT=27017 -e DEST_HOST='mongodb' --link mongodb external-application:latest
It's important to always check the result of docker logs <container-name> --tail 25 -f. From my point of view, I think it is an issue related to permissions on this directory '/bitnami/mongodb'. Check out sameersbn comment how to fix this permission issue.
I'll assume it's the compose specification then. Try the following configuration
environment:
MONGODB_ROOT_PASSWORD:$MONGODB_ROOT_PASSWORD
MONGODB_USERNAME:$MONGODB_USERNAME
MONGODB_PASSWORD:$MONGODB_PASSWORD
MONGODB_DATABASE:$MONGODB_DATABASE
volumes:
- '/data/db:/data/db'
The issue turned out to be that I had changed the password in MONGODB_PASSWORD (it had an # in it so I thought it would have interfered with the string parsing, so I consequently changed it). The problem is, when the container restarts it references the same volume (as it should), so the users were never updated and as a result I was logging in with the wrong credentials.

Why isn't my server restarting / code updating using Docker + Nodejs?

My docker file is super simple:
FROM node:4-onbuild
RUN npm install gulp -g;
EXPOSE 8888
This image will automatically run the start script in package.json which I have set simply as gulp.
If I run gulp on my host machine, and make a change to node file, it automatically restarts server:
var gulp = require('gulp');
var nodemon = require('gulp-nodemon');
gulp.task('default', function() {
nodemon({
script: 'server.js', // starts up server on port 4000
env: { 'NODE_ENV': 'development' }
})
});
Figuring everything is okay I run this: docker run -d -p 1234:4000 -v $(pwd):/usr/src/app my-image
Going to http://192.168.99.100:1234/ shows 'Hello World!' from my server.js file. Updating the file does NOT update what I see by hitting that URL again. If I exec into the container, I see the file is updated. Since the container started node via the same gulp command, I don't understand why the node server wouldn't have restarted and shown the update.
The TL;DR of this is that you need to set nodemon to poll the filesystem for changes as follows: https://github.com/remy/nodemon#application-isnt-restarting
In some networked environments (such as a container running nodemon reading across a mounted drive), you will need to use the legacyWatch: true which enabled Chokidar's polling.
Via the CLI, use either --legacy-watch or -L
The longer version is this (with one key assumption - you're using docker on Mac or similar):
On Mac or similar, docker doesn't run natively and instead runs inside of a virtual machine (generally virtual box via docker-machine). Virtual machines generally don't propagate filesystem inotify events, which is what most watchers rely on to restart or perform an action when a file changes. As the virtual machine doesn't propagate the events from the host, Docker never receives the events. Your original docker file would probably work on a native linux machine.
There's an open issue and much more detailed discussion of this here.

Resources