...
Markdown |
---|
# Introduction - Containerization is about packaging an application, libraries and configuration as an image - The image can be shared easily with others publicly or in a limited scope - Instances of the image (the containers) can be quickly spawned in a portable way - Containers are isolated from the host system and from each other (except when explicitly configured otherwise) - This allows to design a software system as a set of communicating microservices - This also enhances security, because a compromised component does not grant access to the rest of the system # Containerization vs Virtualization - VMs emulate the whole stack (hardware + software), while containers share some of the resources with the host system - VMs require more resources to operate and take longer to start - VMs are usually mutable -- one creates an instance from base image, then installs `X` and configures `Y`, then after a few weeks one installs `Z`, etc. - Containers are immutable -- if you want to update `X` installed, you rebuild the container image and restart it # Container types - A *system container* is one which emulates VM behaviour - It has a main process (`/sbin/init`) able to spawn other daemons and foreground applications - An *application container* has just one application running as its only process - The best practice is to design application containers and connect them using well-defined channels # Basic commands - These examples use Docker as the container engine - To download `ubuntu` image from the public repository: docker pull ubuntu - To list available images: docker images - To run a container from `ubuntu` image: docker run -it ubuntu - The `-it` stands for: -i, --interactive Keep STDIN open even if not attached -t, --tty Allocate a pseudo-TTY - It is required to run interactive commands in a shell - To list running containers: docker ps # Specific images - The public repository called the [**Docker Hub**](https://hub.docker.com/) contains a lot of ready-to-use images - Some of the images contain a base OS: - [Ubuntu](https://hub.docker.com/_/ubuntu) - [CentOS](https://hub.docker.com/_/centos) - [Arch Linux](https://hub.docker.com/_/archlinux) - Some contain compilers, interpreters or other useful tools: - [Python](https://hub.docker.com/_/python) - [Maven](https://hub.docker.com/_/maven) - Others contain known servers or DBMS: - [Apache HTTP Server](https://hub.docker.com/_/httpd) - [PostgreSQL](https://hub.docker.com/_/postgres) - It is always a good idea to get acknowledged with the documentation of each specific image - Creators of these images follow certain conventions to make the usage of image easier # Examples - To run an HTTP server hosting a set of HTML files: echo '<h1>Hello World from Docker!</h1>' > index.html docker run -p 8080:80 -v "$(pwd):/usr/local/apache2/htdocs" httpd - The `-p` and `-v` flags stand for: -p, --publish list Publish a container's port(s) to the host -v, --volume list Bind mount a volume - The `-p 8080:80` allows to bind `localhost:8080` on the host system, to `localhost:80` inside the container - The `-v "$(pwd):/usr/local/apache2/htdocs"` mounts the current directory on the host as `/usr/local/apache2/htdocs` inside the container - To check if your Maven project builds correctly in a clean environment: mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=pl.psnc -DartifactId=docker-training -Dversion=1.0.0 -DinteractiveMode=false docker run -v "$(pwd)/docker-training:/usr/src/docker-training" -w /usr/src/docker-training maven:3-openjdk-8 mvn package - The `-v` is used in a similar way as previously - The `-w /usr/src/docker-training` sets the working directory when the container launches - The image is `maven:3-openjdk-8`, to select specific OpenJDK version - To start a PostgreSQL instance with a pre-defined schema and initial data: $ cat 1-init.sql CREATE TABLE data ( id SERIAL PRIMARY KEY, value INTEGER ); $ cat 2-fill.sql INSERT INTO data (value) VALUES (100), (200), (300), (400), (500); docker run -e POSTGRES_PASSWORD=qwerty -v "$(pwd):/docker-entrypoint-initdb.d" -p 5432:5432 postgres - The `-e POSTGRES_PASSWORD=qwerty` sets value to an environment variable - The `-v` mounts the SQL scripts in a path loaded at runtime - The `-p` is used to publish the default PostgreSQL port from the container to the host - In another terminal, you can check if the database works correctly: - By executing a query inside the container: echo 'SELECT * FROM data' | docker psqlexec --host=localhosti sql-test psql --username=user postgres # Applications with multiple containers - Keep the PostgreSQL container running - Prepare`docker theexec` followingallows `index.php` file: ``` phpto execute a command inside a <?php $dbconn = pg_connect("host=localhost port=5432 user=postgres password=qwerty"); running $resultcontainer = pg_query($dbconn, "SELECT id, value FROM data"); echo "<table><thead><tr><th>Id</th><th>Value</th></tr></thead><tbody>"; - while ($row = pg_fetch_row($result)) { `-i` means that the standard input from the host (the echo "<tr><td>$row[0]</td><td>$row[1]</td></tr>"; } echo "</tbody></table>"; ?> ``` SQL query) is passed to the process in the container - The script- will connect toBy theexecuting database,a selectquery all rows in the host OS: (requires that you have `data` table and create an HTML table with the result - `psql` command Toline run an Apache HTTP server with PHP: tool available) docker run -it -p 8080:80 -v "$(pwd):/var/www/html" php:apache sh -c 'apt-get update; apt-get install -y libpq-dev; docker-php-ext-install pgsql; apache2-foreground' - Take note that `php:apache` uses different default path than `httpd` image used - The command to be run in the container will (1) update packages list, (2) install `libpq-dev`, (3) install `pgsql` PHP extension and finally (4) run the HTTP server - This is not the optimal way of using the `php:apache` image; it would be better to build a new image on top of `php:apache`, but this will be covered in the next tutorial - You can try opening <http://localhost:8080>, however you will only see a connection error - The error appears because the PostgreSQL container is isolated - It publishes its 5432 port to the host system, but not to the other containers - To overcome this, let's create a Docker network: docker network create docker-training - Restart the PostgreSQL container, but (1) put it into the `docker-training` network and (2) name it explicitly docker run -e POSTGRES_PASSWORD=qwerty -v "$(pwd):/docker-entrypoint-initdb.d" -p 5432:5432 --network docker-training --name db postgres - Modify the `index.php` ``` diff @@ -1,5 +1,5 @@ <?php -$dbconn = pg_connect("host=localhost port=5432 user=postgres password=qwerty"); +$dbconn = pg_connect("host=db port=5432 user=postgres password=qwerty"); $result = pg_query($dbconn, "SELECT id, value FROM data"); ``` - Restart the PHP container, but also put it into the `docker-training` network docker run -it -p 8080:80 -v "$(pwd):/var/www/html" --network docker-training php:apache sh -c 'apt-get update; apt-get install -y libpq-dev; docker-php-ext-install pgsql; apache2-foreground' - Now you can see the result in <http://localhost:8080> - Note that you can omit `-p 5432:5432` when running PostgreSQL container unless you want to connect to database from the host # Docker Compose - Running a multi-container application in the way described so far is tedious and error-prone - To address such issues you can use Docker Compose - The whole application is described in a declarative YAML file - The file can be source-controlled in git or SVN and easily shared with others - To recreate the previous application: - Create directories `db` and `web` - Copy `1-init.sql` and `2-fill.sql` to `db/` - Copy `index.php` to `web/` - Create the following `docker-compose.yml` file: ``` yaml version: "3.9" services: db: image: "postgres" environment: - POSTGRES_PASSWORD=qwerty volumes: - "./db:/docker-entrypoint-initdb.d" web: image: "php:apache" command: "sh -c 'apt-get update; apt-get install -y libpq-dev; docker-php-ext-install pgsql; apache2-foreground'" ports: - "8080:80"echo 'SELECT * FROM data' | psql --host=localhost --username=postgres # Applications with multiple containers - Keep the PostgreSQL container running - Prepare the following `index.php` file: ``` php <?php $dbconn = pg_connect("host=localhost port=5432 user=postgres password=qwerty"); $result = pg_query($dbconn, "SELECT id, value FROM data"); echo "<table><thead><tr><th>Id</th><th>Value</th></tr></thead><tbody>"; while ($row = pg_fetch_row($result)) { echo "<tr><td>$row[0]</td><td>$row[1]</td></tr>"; } echo "</tbody></table>"; ?> ``` - The script will connect to the database, select all rows in the `data` table and create an HTML table with the result - To run an Apache HTTP server with PHP: docker run -it -p 8080:80 -v "$(pwd):/var/www/html" php:apache sh -c 'apt-get update; apt-get install -y libpq-dev; docker-php-ext-install pgsql; apache2-foreground' - Take note that `php:apache` uses different default path than `httpd` image used - The command to be run in the container will (1) update packages list, (2) install `libpq-dev`, (3) install `pgsql` PHP extension and finally (4) run the HTTP server - This is not the optimal way of using the `php:apache` image; it would be better to build a new image on top of `php:apache`, but this will be covered in the next tutorial - You can try opening <http://localhost:8080>, however you will only see a connection error - The error appears because the PostgreSQL container is isolated - It publishes its 5432 port to the host system, but not to the other containers - To overcome this, let's create a Docker network: docker network create docker-training - Restart the PostgreSQL container, but (1) put it into the `docker-training` network and (2) name it explicitly docker run -e POSTGRES_PASSWORD=qwerty -v "$(pwd):/docker-entrypoint-initdb.d" -p 5432:5432 --network docker-training --name db postgres - Modify the `index.php` ``` diff @@ -1,5 +1,5 @@ <?php -$dbconn = pg_connect("host=localhost port=5432 user=postgres password=qwerty"); +$dbconn = pg_connect("host=db port=5432 user=postgres password=qwerty"); $result = pg_query($dbconn, "SELECT id, value FROM data"); ``` - Restart the PHP container, but also put it into the `docker-training` network docker run -it -p 8080:80 -v "$(pwd):/var/www/html" --network docker-training php:apache sh -c 'apt-get update; apt-get install -y libpq-dev; docker-php-ext-install pgsql; apache2-foreground' - Now you can see the result in <http://localhost:8080> - Note that you can omit `-p 5432:5432` when running PostgreSQL container unless you want to connect to database from the host # Docker Compose - Running a multi-container application in the way described so far is tedious and error-prone - To address such issues you can use Docker Compose - The whole application is described in a declarative YAML file - The file can be source-controlled in git or SVN and easily shared with others - To recreate the previous application: - Create directories `db` and `web` - Copy `1-init.sql` and `2-fill.sql` to `db/` - Copy `index.php` to `web/` - Create the following `docker-compose.yml` file: ``` yaml version: "3.9" services: volumesdb: image: - "./web:/var/www/html"postgres" ``` - In thisenvironment: file you described `db` and `web` services - By default Docker Compose will create a new Docker network and put - POSTGRES_PASSWORD=qwerty both containers insidevolumes: under these names - Take note that in the `volumes` section you can use relative paths - "./db:/docker-entrypoint-initdb.d" (command line Docker requires absolute paths) - web: You can put into a git repository all files i.e. the `db/` and image: "php:apache" `web/` directories and the YAML file - Tocommand: run"sh the Docker Compose application docker compose up - Please note, that unlike `docker run` the containers started by-c 'apt-get update; apt-get install -y libpq-dev; docker-php-ext-install pgsql; apache2-foreground'" ports: Docker Compose are not destroyed but just stopped when you exit the- "8080:80" above command - This has consequences for certain images; for example, `postgres` volumes: image does not read `/docker-entrypoint-initdb.d/` if it detects - "./web:/var/www/html" that there``` - is a databaseIn already;this iffile you startdescribed Docker`db` Compose,and then `web` services - stopBy itdefault toDocker modifyCompose thewill initialcreate SQL,a thennew youDocker neednetwork toand runput both containers inside `dockerunder compose rm` to enforce removal of containers and restart from scratch the next timethese names - Take note that in the `volumes` section you can use relative paths (command line Docker requires absolute paths) - You can put into a git repository all files i.e. the `db/` and `web/` directories and the YAML file - To run the Docker Compose application docker compose up - Please note, that unlike `docker run` the containers started by Docker Compose are not destroyed but just stopped when you exit the above command - This has consequences for certain images; for example, `postgres` image does not read `/docker-entrypoint-initdb.d/` if it detects that there is a database already; if you start Docker Compose, then stop it to modify the initial SQL, then you need to run `docker compose rm` to enforce removal of containers and restart from scratch the next time |
...
This work has been carried out within the framework of the EUROfusion Consortium, funded by the European Union via the Euratom Research and Training Programme (Grant Agreement No. 101052200—EUROfusion). Views and opinions expressed are however those of the author(s) only and do not necessarily reflect those of the European Union or the European Commission. Neither the European Union nor the European Commission can be held responsible for them. The scientific work is published for the realization of the international project co-financed by Polish Ministry of Science and Higher Education in 2021 from financial resources of the program entitled "PMW” 5218/HEU - EURATOM/2022/2