How to change a container's source code and generate a new image from it?

9/29/2021

I have deployed this using Kubernetes:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: voting-app-deploy
  labels:
    name: voting-app-deploy
    app: demo-voting-app
spec:
  replicas: 1
  selector:
    matchLabels:
      name: voting-app-pod
      app: demo-voting-app
    
  template:
    metadata:
      name: voting-app-pod
      labels:
        name: voting-app-pod
        app: demo-voting-app
    spec:
      containers:
        - name: voting-app
          image: kodekloud/examplevotingapp_vote:v1
          ports:
            - containerPort: 80

With this Service:

apiVersion: v1
kind: Service
metadata:
  name: voting-service
  labels:
    name: voting-service
    app: demo-voting-app
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30004
  selector:
    name: voting-app-pod
    app: demo-voting-app

After checking if the Pods are running, I get a shell to the running container where I can see the source code and make changes using vi:

kubectl exec -it podsname -- sh

The problem is whenever I destroy the deployment and bring it back I lose all my changes in the source code.

This is the application's Dockerfile, which I got from within the running container:

# Using official python runtime base image
FROM python:2.7-alpine

# Set the application directory
WORKDIR /app

# Install our requirements.txt
ADD requirements.txt /app/requirements.txt
RUN pip install -r requirements.txt

# Copy our code from the current folder to /app inside the container
ADD . /app

# Make port 80 available for links and/or publish
EXPOSE 80

# Define our command to be run when launching the container
CMD ["gunicorn", "app:app", "-b", "0.0.0.0:80", "--log-file", "-", "--access-logfile", "-", "--workers", "4", "--keep-alive", "0"]

So, how can I get this source code and generate a new image?

-- Kaio H. Cunha
docker
dockerfile
kubernetes

4 Answers

9/29/2021

You should use volumes.

Volumes will allow you to map the src code folder in the container to a path outside of your container and it will not be destroyed during restart since it will be store outside of your container.

You can see a working sample with explanation here: https://github.com/nirgeier/KubernetesLabs/tree/master/Labs/09-StatefulSet

enter image description here


emptyDir

emptyDir is the default volume and it's being deleted when the pod is deleted, but I'm using it here to show you a sample on how to use volumes.

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}
-- CodeWizard
Source: StackOverflow

9/29/2021

Quick and dirty

If you change your code often and this isn't a production environment you can use the --reload that's according to this thread is available since gunicorn 19.0. this option makes the server reload your code changes into new workers.

Then when you want to save your new image you can use docker commit to "commit" or save your changes to a new image like so:

docker commit running_container_name_or_id imagerepo/image-name:tag

Though this isn't that recommended in the long run just for small fixes and checks.

Long term preferred usage If you want a long term method method either develop locally with a mounted bind-mount (map a folder from the host machine to the docker container) and build a final image for kubernetes, or you could mount your source code using a config map like in this example:

apiVersion: v1
kind: ConfigMap
metadata:
  name: file-configmap
  namespace: default
data:
  config.conf: |
    [mysqld]
    binlog-format=mixed

Pod (usage in deployment is the same):

apiVersion: v1
kind: Pod
metadata:
  name: 
spec:
  containers:
    - name: container-name
      image: image-name
      volumeMounts:
      - name: my-file-volume
        mountPath: /path/in/container/config.conf
        subPath: config.conf
  volumes:
    - name: my-file-volume
      configMap:
        name: file-configmap
-- Noam Yizraeli
Source: StackOverflow

9/30/2021

In you container(kubectl exec), use ps | grep gunicorn find the process of your application. Killed it then start it again.

-- vincent pli
Source: StackOverflow

10/1/2021

I've found a solution to my problem that's not common, I guess. I have to clarify that i'm not working on production environment. I just want to "steal" the source code from an image, since I don't have access to the repository.

Here is what I did:

1 . Created a folder and subfolder to organize the code. 2. Got a shell inside the pod's container:

docker exec -it containername sh

or

kubectl exec -it podsname -- sh
  1. From the shell, edited what I wanted to edit in the source code.
  2. Copied the content of the app folder IN THE CONTAINER to my local subfolder(called 'dados'):
cp -R /app /dados/
  1. Created a Dockerfile in the folder that's based on the image I want to edit, then run a command to remove the /app folder, and finally, a command to copy the contents of my local folder to the new image I was going to build.
FROM kodekloud/examplevotingapp_vote:v1
RUN rm -rf /app/*.*
COPY ./dados/app /app
  1. Created a new repository on DockerHub called 'myusername/my-voting-app' to receive my new image.
  2. Built the new image:
docker build -t myusername/my-voting-app .
  1. Tagged the image with my repo's name:
docker tag kaiohenricunha/my-voting-app kaiohenricunha/my-voting-app
  1. Sent to dockerhub:
docker push kaiohenricunha/my-voting-app
  1. Now on Kubernetes, changed my pod's container image to the new image I had built and deployed them.
-- Kaio H. Cunha
Source: StackOverflow