Virtualization

Virtual Environments

echo $PATH | tr : '\n' | grep conda
echo "python is" $(which python)

/Users/tut/conda/envs/sys/bin
/Users/tut/conda/condabin
python is /Users/tut/conda/envs/sys/bin/python

Package Installation

  1. Create a new virtual environment called example: conda create -n example python=3.12
  2. Activate that virtual environment: conda activate example
  3. Install the faker package: pip install faker
find $HOME/conda/envs/example -name faker
/Users/tut/conda/envs/example/bin/faker
/Users/tut/conda/envs/example/lib/python3.12/site-packages/faker
#!/Users/gregwilson/conda/envs/example/bin/python3.12
# -*- coding: utf-8 -*-
import re
import sys
from faker.cli import execute_from_command_line
if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(execute_from_command_line())

Exercises

What is the re.sub call in the faker script doing and why?

Limits of Virtual Environments

Docker

docker image ls
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE
docker container ls
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

Common Error Message

docker image ls
Cannot connect to the Docker daemon at unix:///Users/tut/.docker/run/docker.sock.
Is the docker daemon running?

Running a Container

$ docker container run ubuntu:latest
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
bccd10f490ab: Pull complete 
Digest: sha256:77906da86b60585ce12215807090eb327e7386c8fafb5402369e421f44eff17e
Status: Downloaded newer image for ubuntu:latest

$ docker container ls

$ docker container ls -a
CONTAINER ID   IMAGE           COMMAND       CREATED          STATUS                     PORTS     NAMES
741bb295734f   ubuntu:latest   "/bin/bash"   10 seconds ago   Exited (0) 9 seconds ago             xenodochial_mclaren

$ docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
ubuntu       latest    ca2b0f26964c   3 weeks ago   77.9MB

Re-running a Container

$ docker container run ubuntu:latest pwd
/

$ docker container run ubuntu:latest ls
bin
boot
dev
…more entries…
usr
var

This Doesn't Work

$ docker container run ubuntu:latest "echo hello"
docker: Error response from daemon: \
failed to create task for container: \
failed to create shim task: \
OCI runtime create failed: \
runc create failed: \
unable to start container process: \
exec: "echo hello": executable file not found in $PATH: unknown.

Pulling Images

$ docker pull ubuntu:latest
latest: Pulling from library/ubuntu
Digest: sha256:77906da86b60585ce12215807090eb327e7386c8fafb5402369e421f44eff17e
Status: Image is up to date for ubuntu:latest
docker.io/library/ubuntu:latest

What Have We Got?

$ docker container run ubuntu cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.4 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.4 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

Labels Can Change

Wait, What's a Layer?

How layers and copy-on-write work in Docker
Figure 1: Docker Layers and Copy-on-Write

Inside the Container

$ docker container run -i -t ubuntu
root@4238b3b51abd:/# pwd
/
root@4238b3b51abd:/# whoami
root
root@4238b3b51abd:/# ls
bin  boot  dev  etc  home  lib  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@4238b3b51abd:/# exit
exit
$

Persistence

$ docker container run -i -t ubuntu
root@a8ea570a84d3:/# ls /tmp
root@a8ea570a84d3:/# touch /tmp/proof-we-were-here.txt
root@a8ea570a84d3:/# ls /tmp
proof-we-were-here.txt
root@a8ea570a84d3:/# exit
exit

$ docker container run -i -t ubuntu
root@f792c15ebb5b:/# ls /tmp
root@f792c15ebb5b:/# exit
exit

What Is Running

$ docker container ls --format "table {{.ID}}\t{{.Status}}" |
CONTAINER ID   STATUS                                       |
                                                            |
                                                            | $ docker container run -it ubuntu
                                                            | root@a5427ccdeb26:/#
                                                            |
$ docker container ls --format "table {{.ID}}\t{{.Status}}" |
CONTAINER ID   STATUS                                       |
a5427ccdeb26   Up 38 seconds                                |
                                                            |
                                                            | root@a5427ccdeb26:/# exit
                                                            | exit
                                                            |
$ docker container ls --format "table {{.ID}}\t{{.Status}}" |
CONTAINER ID   STATUS                                       |

IDs Only

$ docker container ls -a -q
22b7c4109157
8640cfb5e07a
4c1ffdcb1c88
37f30320bc8b
fa9f02841fe9

Filtering

$ docker image ls --filter reference="ubuntu"
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
ubuntu       latest    2b7cc08dcdbb   6 weeks ago   69.2MB

Installing Software

$ docker container run -it ubuntu

# apt update
…lots of output…

# apt install -y python3
…lots of output…

# which python

# which python3
/usr/bin/python3

# python3 --version
Python 3.10.12
# exit
exit

$ docker run -it ubuntu

# which python

# exit
exit

Actually Installing Software

FROM ubuntu:latest

RUN apt update
RUN apt install python3 -y
$ docker build -t gvwilson/ubuntu-python3 ubuntu-python3

#0 building with "desktop-linux" instance using docker driver

#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 99B done
#1 DONE 0.0s

#2 [internal] load metadata for docker.io/library/ubuntu:latest
#2 DONE 1.6s

#3 [internal] load .dockerignore
#3 transferring context: 2B done
#3 DONE 0.0s

#4 [1/3] FROM docker.io/library/ubuntu:latest@sha256:77906da86b60585ce12215807090eb327e7386c8fafb5402369e421f44eff17e
#4 resolve docker.io/library/ubuntu:latest@sha256:77906da86b60585ce12215807090eb327e7386c8fafb5402369e421f44eff17e done
#4 DONE 0.0s

#5 [2/3] RUN apt update
#5 CACHED

#6 [3/3] RUN apt install python3 -y
#6 CACHED

#7 exporting to image
#7 exporting layers done
#7 writing image sha256:c06d47d8275d4ef724dad192bf72daaac6b86701a1be40e1ac03f53092201d71 done
#7 naming to docker.io/gvwilson/ubuntu-python3 done
#7 DONE 0.0s
$ docker container run -it gvwilson/ubuntu-python3
# which python3
/usr/bin/python3

Inspecting Containers

$ docker container inspect 56d9f83286f9
…199 lines of JSON…

Layers

$ docker image history gvwilson/ubuntu-python3
IMAGE          CREATED        CREATED BY                                      SIZE      COMMENT
c06d47d8275d   24 hours ago   RUN /bin/sh -c apt install python3 -y # buil…   29.5MB    buildkit.dockerfile.v0
<missing>      24 hours ago   RUN /bin/sh -c apt update # buildkit            45.6MB    buildkit.dockerfile.v0
<missing>      3 weeks ago    /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>      3 weeks ago    /bin/sh -c #(nop) ADD file:07cdbabf782942af0…   69.2MB
<missing>      3 weeks ago    /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B
<missing>      3 weeks ago    /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B
<missing>      3 weeks ago    /bin/sh -c #(nop)  ARG LAUNCHPAD_BUILD_ARCH     0B
<missing>      3 weeks ago    /bin/sh -c #(nop)  ARG RELEASE                  0B
$ docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          1         1         144.8MB   0B (0%)
Containers      1         0         14B       14B (100%)
Local Volumes   1         0         0B        0B
Build Cache     5         0         62B       62B

Choosing a Command

FROM ubuntu:latest

RUN apt update
RUN apt install python3 -y

CMD ["python3", "--version"]
$ docker build -t gvwilson/python3 python3-version
…lots of output…
$ docker container run gvwilson/python3-version
Python 3.10.12
FROM ubuntu:latest

RUN apt update
RUN apt install python3 -y

CMD ["python3", "-i"]
$ docker container run -it gvwilson/python3-interactive
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> print("hello")
hello
>>> exit()
$ 

Copying Files Into Images

print("proof that the script was copied")
FROM ubuntu:latest

RUN apt update
RUN apt install python3 -y

COPY proof.py /home

CMD ["python3", "/home/proof.py"]
$ docker build -t gvwilson/python3-script python3-script
…output…

$ docker container run gvwilson/python3-script
proof that the script was copied

Order Matters

Exericse

  1. Create a Dockerfile that installs Git and uses it to clone a repository containing a Python script as the image is being built, then runs that Python script by default.

  2. What is the difference between CMD and ENTRYPOINT in Dockerfiles? When would you use the latter instead of the former?

Sharing Files

$ mkdir -p /tmp/mount_example

$ echo "proof that mounting works" > /tmp/mount_example/test.txt

$ docker container run -it --mount type=bind,source=/tmp/mount_example,target=/example ubuntu

# ls /example
test.txt

# cat /example/test.txt
proof that mounting works

# cp /example/test.txt /example/copied.txt

# exit

$ ls /tmp/mount_example/
copied.txt  test.txt

Environment Variables

FROM ubuntu:latest

CMD ["echo", "variable is '${ECHO_VAR}'"]
$ ECHO_VAR=some_string docker container run gvwilson/ubuntu-env-var
variable is '${ECHO_VAR}'
#!/usr/bin/env bash
echo "variable is '${ECHO_VAR}'"
FROM ubuntu:latest

COPY show_var.sh /home
CMD ["/home/show_var.sh"]
$ docker container run gvwilson/ubuntu-env-var-succeeds
variable is ''

$ ECHO_VAR='it worked' docker container run gvwilson/ubuntu-env-var-succeeds
variable is ''

$ ECHO_VAR='it worked' docker container run -e ECHO_VAR gvwilson/ubuntu-env-var-succeeds
variable is 'it worked'

Environment Files

ECHO_VAR=this is set in a .env file
$ docker container run --env-file ./set_echo_var.env gvwilson/ubuntu-env-var-succeeds
variable is 'this is set in a .env file'

Long-Running Containers

docker container ls -a --format "table {{.ID}}\t{{.Status}}" | head -n 5
CONTAINER ID   STATUS
56d9f83286f9   Exited (0) 3 minutes ago
3a48286cb202   Exited (0) 7 minutes ago
24e164d06d47   Exited (0) 5 hours ago
6e44181432fd   Exited (0) 5 hours ago

Long-Lived Service

#!/usr/bin/env bash
COUNTER=1
while true
do
    echo $COUNTER $(date "+%H:%M:%S")
    COUNTER=$(expr $COUNTER + 1)
    sleep 1
done
FROM ubuntu:latest

COPY count_time.sh /home
CMD ["/home/count_time.sh"]
$ docker container run gvwilson/count-time
1 18:38:10
2 18:38:11
3 18:38:12
4 18:38:13
…and so on…
$ docker ps
CONTAINER ID   IMAGE                 COMMAND                 CREATED         STATUS
741d896e4bb3   gvwilson/count-time   "/home/count_time.sh"   7 seconds ago   Up 6 seconds

$ docker kill 741d

A Better Way

$ docker container run --detach gvwilson/count-time
54c8c682a94a3853c62e2f86c19d463428a01452ed7e5cf85b076dcc0f447474

$ docker ps
CONTAINER ID   IMAGE                 COMMAND                 CREATED          STATUS
54c8c682a94a   gvwilson/count-time   "/home/count_time.sh"   12 seconds ago   Up 11 seconds

$ docker container stop 54c8
…wait a few seconds…

$
$ docker logs 54c8
1 18:42:44
2 18:42:45
3 18:42:46
4 18:42:47
5 18:42:48
…more output…