Should i dockerize django app as non-root? - node.js

Should i dockerize Django app as a root user? If yes how can i set up non-root user for Django? Because in node.js app should have USER:node which is a better practice.
Code example from official docker page which does not include non-root:
FROM python:3
ENV PYTHONUNBUFFERED=1
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/

It's generically a good practice.
At the start of your Dockerfile, before you COPY anything in, create the user. It doesn't need to have any specific properties and it doesn't need to match any specific host user ID. The only particular reason to do this early is to avoid repeating it on rebuilds.
At the end of your Dockerfile, after you run all of the build steps, only then switch USER to the new user. The code and any installed libraries will be owned by the root user; and that's good, because it means the application can't accidentally overwrite the application code.
FROM python:3
# Create the non-root user. Doing this before any COPY means it won't
# be repeated on rebuild, for marginal savings in space and rebuild time.
# The user can have any name and any uid; it does not need to match any
# particular host system where the image might run.
RUN adduser --system --no-create-home someuser
# Install the application as in the question (still as root).
ENV PYTHONUNBUFFERED=1
WORKDIR /code
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# Explain how to run the container. Only switch to the non-root user now.
EXPOSE 8000
USER someuser
CMD ["./main.py"]
Do not try to write files inside the container; instead, use a separate database container for persistence. Do not pass a host user ID as a build argument. Do not configure a password for the user or otherwise attempt to set up interactive logins. Do not create a home directory; it won't be used.

Related

failed run Postgres in alpine dockerfile

i am trying to deploy my Go app with Alpine in docker, I was able to use it on my Mac and then going to Production with Centos 8 got issues
here is my Dockerfile:
FROM golang:alpine
RUN apk add --no-cache postgresql
RUN apk update && apk add --no-cache gcc && apk add --no-cache libc-dev && apk add --no-cache --update make
# Set the current working Directory inside the container
WORKDIR /app
# Copy go mod and sum files
COPY go.mod go.sum ./
# Download all dependencies. they will be cached of the go.mod and go.sum files are not changed
RUN go mod download
# Copy the source from the current directory to the WORKDIR inisde the container
COPY . .
# Build the Go app
RUN go build .
RUN rm -rf /usr/local/var/postgres/postmaster.pid
// this commands below like "psql -c ;'DROP DATABASE IF EXISTS prod'"
// "psql -c ;'CREATE USER prod'"
RUN make setup
# Exporse port 3000 or 8000 to the outisde world
EXPOSE 3000..
CMD ["make", "run" ]
then i got error:
psql: error: could not connect to server: could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/tmp/.s.PGSQL.5432"?
on my make setup i do the migration, create user, database
can make SUPERUSER on psql for that alpine also??
what u can see on the above syntax, is there any wrong and how to correct it? I have stuck from yesterday
Delete your original docker file's from 8th line to 20th and add these.
If your folder structure like this :
- directory
|
-> Dockerfile
-> go.mod
-> go.sum
-> go source files
# Copy go mod and sum files
COPY . /app
# Set the current working Directory inside the container
WORKDIR /app
RUN go mod download
RUN go build .
You cannot run database commands in a Dockerfile.
By analogy, consider the go generate command: you can embed special comments in your Go source code that ask the Go compiler to run programs for you, typically to generate other source files. Say you //go:generate: psql ... in your source code and run go generate ... && go install . Now you run that compiled binary on a different system. Since you're not pointing at the same database any more, the database setup is lost.
In the same way, a Dockerfile produces a compiled artifact (in this case the Docker image) and it needs to run independently of its host environment. In your example you could docker push the image you built on MacOS to a registry, and docker run it from the CentOS host without rebuilding it (and that's probably better practice for a production system).
For the specific commands you show in the question, you could put them in a database container's /docker-entrypoint-initdb.d directory, or otherwise just run them once pointing at your database. For more general-purpose database setup you might look at running a database migration tool at application startup, either in your program's main() function or in a wrapper entrypoint script.

how to pass file to docker image?

I have an application which I have dockerize it. The application recieves a configuration file which can change per user. I used the following command to pass config file to the image.
docker run --rm --name $(PROJECT_NAME) -v $(pwd)/config.yaml:/root/config.yaml
I don't want to build the image again for a new config file. but it doesn't seems to work as when I inspected the contents of the file on the container it showed me the same file. It seems that when I change the file on my local system and pass it to the container in run command, the change is not reflected and it loads previous config file. Any ideas what I might be doing wrong or any best practices to deal with such a scenario.
Dockerfile
# base image
FROM amazonlinux:2
# copy requirements file to working directory
COPY requirements.txt ./
RUN yum -y install python3 \
&& pip3 install --no-cache-dir -r requirements.txt
COPY app/. ./
ENTRYPOINT python3 run.py config.yaml
I was able to resolve it. Here is my updated Docker file : I have put my config file in config/ folder and mount it. Now I can build the image once and can change the config file inside the config/ folder and changes will be reflected inside the container.
# base image
FROM amazonlinux:2
# copy requirements file to working directory
COPY requirements.txt ./
COPY app/. ./
RUN yum -y install python3 \
&& pip3 install --no-cache-dir -r requirements.txt
ENTRYPOINT python3 run.py config/config.yaml
This is the run command:
docker run --rm --name $(PROJECT_NAME) -v $(CONFIG_PATH):/config $(DOCKER_REPO)
CONFIG_PATH = $(PWD)/config
What you see is "normal".
When you mount a file or directory, you actually mount a pointer (inode) inside the container. It's the way docker work with volume.
Some/most editor use a "safe write" method that consist to write a new file and replace the old file with the new one. When the editor do that, the new updated file have a new pointer (inode) and the old one still exist pointing to the old content.
On linux, a file is only deleted when no one use it, it's why you can't see it but the container still have it.
You will have the same problem if you mount a directory and you delete it and recreate one with the same name, the container will not see the new directory.
It's a know limitation of the bind mount and the best you can do is to mount a directory with your file inside. The other way is to restart your container, Docker will find the new inode and use it.

How to dynamically change content in node project run through docker

I have an angularjs application, I'm running using docker.
The docker file looks like this:-
FROM node:6.2.2
RUN npm install --global gulp-cli && \
npm install --global bower
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/
COPY bower.json /usr/src/app/
RUN npm install && \
bower install -F --allow-root --config.interactive=false
COPY . /usr/src/app
ENV GULP_COMMAND serve:dist
ENTRYPOINT ["sh", "-c"]
CMD ["gulp $GULP_COMMAND"]
Now when I make any changes in say any html file, It doesn't dynamically loads up on the web page. I have to stop the container, remove it, build the image again, remove the earlier image and then restart the container from new image. Do I have to do this every time? (I'm new to docker, and I guess this issue is coz my source code is not put into volume, but I don't know how to do it using docker file)
You are correct, you should use volumes for stuff like this. During development, give it the same volumes as the COPY directories. It'll override it with whatever is on your machine, no need to rebuild the image, or even restart the container. Perfect for development.
When actually baking your images for production, you remove the volumes, leave the COPY in, and you'll get a deterministic container. I would recommend you read through this article here: https://docs.docker.com/storage/volumes/.
In general, there are 3 ways to do volumes.
Define them in your dockerfile using VOLUME.
Personally, I've never done this. I don't really see the benefits of this against the other two methods. I believe it would be more common to do this when your volume is meant to act as a permanent data-store. Not so much when you're just trying to use your live dev environment.
Define them when calling docker run.
docker run ... -v $(pwd)/src:/usr/src/app ...
This is great, cause if your COPY in your dockerfile is ./src /usr/src/app then it temporarily overrides the directory while running the image, but it's still there for deployment when you don't use -v.
Use docker-compose.
My personal recommendation. Docker compose massively simplifies running containers. For sake of simplicity just calls docker run ... but automates the arguments based on a given docker-compose.yml config.
Create a dev service specifying the volumes you want to mount, other containers you want it linked to, etc. Then bring it up using docker-compose up ... or docker-compose run ... depending on what you need.
Smart use of volumes will DRAMATICALLY reduce your development cycle. Would really recommend looking into them.
Yes, you need to rebuild every time the files change, since you only modify the files that are outside of the container. In order to apply the changes to the files IN the container, you need to rebuild the container.
Depending on the use case, you could either make the Docker Container dynamically load the files from another repository, or you could mount an external volume to use in the container, but there are some pitfalls associated with either solution.
If you want to keep your container running as you add your files you could also use a variation.
Mount a volume to any other location e.g. /usr/src/staging.
While the container is running, if you need to copy new files into the container, copy them into the location of the mounted volume.
Run docker exec -it <container-name> bash to open a bash shell inside the running container.
Run a cp /usr/src/staging/* /usr/src/app command to copy all new files into the target folder.

docker-node: Running as non-root user, file permissions

Following docker-node’s best practices, I want to run my node app as non-root user. The recommendation is as follows:
FROM node:6.10.3
...
# At the end, set the user to use when running this image
USER node
My simplified Dockerfile currently looks like this:
FROM node:6.10.3
WORKDIR /opt/app
COPY package.json .
RUN npm install
COPY . .
EXPOSE 3000
USER node
CMD ["node", "server.js"]
So, all the files added during image build are owned by root, but node server.js is run as the node user. This seems to work fine.
My question: Is there any additional security benefit from chown-ing the files so that they belong to node instead of root? I.e. doing something like:
RUN chown -R node:node .
It definitely does, however I would also remove the chown binary (as well as all admin tools). This would make it harder when someone accesses the container as e.g. root. See here for a related answer.
Also, see this Dockerfile for inspiration.

How should I Accomplish a Better Docker Workflow?

Everytime I change a file in the nodejs app I have to rebuild the docker image.
This feels redundant and slows my workflow. Is there a proper way to sync the nodejs app files without rebuilding the whole image again, or is this a normal usage?
It sounds like you want to speed up the development process. In that case I would recommend to mount your directory in your container using the docker run -v option: https://docs.docker.com/engine/userguide/dockervolumes/#mount-a-host-directory-as-a-data-volume
Once you are done developing your program build the image and now start docker without the -v option.
What I ended up doing was:
1) Using volumes with the docker run command - so I could change the code without rebuilding the docker image every time.
2) I had an issue with node_modules being overwritten because a volume acts like a mount - fixed it with node's PATH traversal.
Dockerfile:
FROM node:5.2
# Create our app directories
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
RUN npm install -g nodemon
# This will cache npm install
# And presist the node_modules
# Even after we are using the volume (overwrites)
COPY package.json /usr/src/
RUN cd /usr/src && npm install
#Expose node's port
EXPOSE 3000
# Run the app
CMD nodemon server.js
Command-line:
to build:
docker build -t web-image
to run:
docker run --rm -v $(pwd):/usr/src/app -p 3000:3000 --name web web-image
You could have also done something like change the instruction and it says look in the directory specified by the build context argument of docker build and find the package.json file and then copy that into the current working directory of the container and then RUN npm install and afterwards we will COPY over everything else like so:
# Specify base image
FROM node:alpine
WORKDIR /usr/app
# Install some dependencies
COPY ./package.json ./
RUN npm install
# Setup default command
CMD ["npm", "start"]
You can make as many changes as you want and it will not invalidate the cache for any of these steps here.
The only time that npm install will be executed again is if we make a change to that step or any step above it.
So unless you make a change to the package.json file, the npm install will not be executed again.
So we can test this by running the docker build -t <tagname>/<project-name> .
Now I have made a change to the Dockerfile so you will see some steps re run and eventually our successfully tagged and built image.
Docker detected the change to the step and every step after it, but not the npm install step.
The lesson here is that yes it does make a difference the order in which all these instructions are placed in a Dockerfile.
Its nice to segment out these operations to ensure you are only copying the bare minimum.

Resources