C'est la commande qui s'exécutent lorsque le conteneur est démarre à partir de l'image.
Deux façons possible de l'écrire:
exec form:
```Dockerfile
ENTRYPOINT ["mon executable", "arg1", "arg2"]
```
C'est la façon préféré de faire.
shell form:
```Dockerfile
ENTRYPOINT ma commande 1 && ma commande 2
```
nécessaire lorsque l'on a plusieurs commande à faire tourner de façon séquentielle.
## Construire une image à partir d'un Dockerfile
Une image se construit à partir d'un Dockerfile et d'un contexte (c'est un dossier qui correspond au **.** que tu as mis dans to Dockerfile). Souvent le contexte c'est `.` et il n'y a pas besoin de spécifier l'emplacement du Dockerfile, si il s'appelle `Dockefile` et est à la racine du contexte. Pour constuire l'image on utilise alors:
```bash
docker build .-t <le nom de mon image>:<tag>
```
Le tag sert à spécifier plusieurs version de la même image, un peu comme des branches sur git.
## Push une image sur un registry
Un registry c'est juste un espace de stockage disponible pour héberger des images Docker.
```bash
# On se log au registry (les identifiants sont sur le bitwarden)
docker login registry.viarezo.fr
# On build l'image avec le nom du registry dedans
docker build .-t registry.viarezo.fr/<nom du projet>/<image>:<tag>
# On push l'image sur le registry
docker push registry.viarezo.fr/<nom du projet>/<image>:<tag>
```
Attention il faut avoir créé le projet au préalable sur le registry, en cochant bien l'option public.
## Faire tourner un conteneur à partir d'une image
Une fois qu'on a construit notre image, on peut l'utiliser pour créer autant de conteneurs que l'on veut:
```bash
docker run <image>
# En forwardant un port
docker run -p <port sur mon pc>:<port dans le conteneur> <image>
# En ajoutant des variables d'environnement
docker run -eFOO=BAR <image>
# Avec un .env
docker run --env-file .env <image>
```
## Gérer les conteneurs qui s'exécutent
```bash
# Voir les conteneurs qui tournent et les informations associées
docker ps
# Stopper un conteneur
docker stop <conteneur>
# Redémarrer un conteneur stoppé
docker start <conteneur>
# Supprimer un conteneur stoppé
docker rm <conteneur>
```
## Débugger un conteneur
```bash
# Voir les logs
docker logs <conteneur>
# Executer une commande dans un conteneur
docker exec-it <conteneur> <commande>
# Récupérer un shell dans un conteneur (attention bash ne marche pas pour les images alpine)
@@ -59,6 +59,7 @@ Cela veut dire que si vous ne spécifiez pas explicitement de namespace lorsque
Et cette application c'est VRoum.
Pour cette première partie, l'objectif est de construire les images qui vont faire tourner notre site, il y a deux images à vous répartir (ou à faire ensemble séquentiellement).
Pour un petit pense-bête sur Docker, n'hésite pas à lire [cette petite fiche](./Docker.md)
### Le Front
...
...
@@ -94,214 +95,6 @@ Vérifie que ce n'est pas le cas ou alors fait les changements nécéssaires.
On préfère souvent utiliser une image alpine à une image debian en production, car celle-ci sont beaucoup plus légère. Néanmoins la gestion des dépendences, est souvent plus difficile.
Utilise une image alpine (python:3.8-alpine) comme base pour ton image.
## 1. (Optional) Build and launch the app locally
> This task is optional, don't loose time on it right now!
### Why
If you truly want to immerse yourself in the life of a developer, you will need to be able to iterate quickly on the app locally.
### What
Be creative, try to modify a simple thing in the app.
### How
For this you simply need the `go` cli installed and some knownledge of this language.
When you are happy with the result, you can launch the app with `go run main.go`, or build a binary with `go build`.
### Checks
- [ ] I can run the app locally, and see the web UI.
- [ ] I have implemented a small change in the application and it still runs
## 2. Build a container image (Docker)
### Why
While you build and iterate on your app locally, you need to be able to deploy it on a real production environment.
Since you don't know where it will run (isolated virtual machine, which packages are installed), we want to ensure the reproductability and isolation of the application. That's why containers, that `docker` helps build and run, are made for!
It is a standard API to build and ship applications across diverse workloads. Whatever the server it is running on, you _image_ should always construct the same isolated environment.
Moreover, it is way less expensive in resources (CPU, RAM) than a Virtual Machine, which acheives an isolation by reinstalling a whole OS.
### What
We need to _build_ a container _image_ from the code in this repository. For this, the command `docker build -t <image-name>:<version> .` builds an image from a local _recipe_ `Dockerfile`.
For example, for a simple python application, it could be:
```Dockerfile
FROM python:3.8
COPY requirements.txt .
RUN pip install-r requirements.txt
COPY main.py main.py
CMD ["python", "main.py"]
```
You can find the complete [Dockerfile reference](https://docs.docker.com/engine/reference/builder/) here.
Here we have a _webservice_ written in _golang_, running an HTTP server on the port `3000`.
It serves some static files (stored in `/public`), for the UI. You will mainly access it through a `GET /` for the UI, but there are other routes to manage the state of the app.
### How
You can follow such [a tutorial](https://docs.docker.com/language/golang/build-images/)
1. Write a `Dockerfile`. You need to start from a _base _image_, ideally with golang already install.
2. In the `Dockerfile`, download the microservice's dependencies. Since latest golang version, we only need `go.mod` and `go.sum` for this task.
3. In the `Dockerfile`, build the microservice. You need the command `go build` for this.
4. In the `Dockerfile`, add the `public` folder inside the container, in the same `public` folder.
```Dockerfile
COPY ./public public
```
4. When the container starts, run the microservice.
5. Build a container image: `docker build -t guestbook:v0.1.0 .`
6. Run the container.
You need to _expose_ the port of your application, which run on `3000`. For this, you just need to add the `--publish <external-port>:<internal-port>` to the `docker run` command.
```bash
docker run --publish 3000:3000 guestbook:v0.1.0
```
7. Check that the microservice responds to requests on
http://localhost:3000. You should see the following UI:

8.**Optional**: Implement some best practices, such as "multi-stage builds". It help reduce the size of your images, and increase security.
The image you built so far is pretty large because it contains the entire Go
toolkit. It's time to make it smaller. Much smaller. Here is what you need to
do:
1. Check to see how big your container image is.
2. Change the `go build` command to make the binary statically linked (if you
don't know what that means, just ask!).
3. In your `Dockerfile`, create a second stage that starts from `scratch`.
4. Copy the binary from the first stage to the second.
5. In the second stage, run the microservice.
6. Build your container image again.
7. Check to see how big the image is now.
### Checks
- [ ] I can build an image locally
- [ ] I can run a the container locally
- [ ] I can access the web interface locally
<details>
<summary><em>Compare your work to the solution before moving on. Are there differences? Is your approach better or worse? Why?</em></summary>
You can find the complete solution [here](https://github.com/padok-team/dojo-guestbook/blob/feat/solution/Dockerfile). Don't spoil yourself too much!
</details>
## 3. Run it locally with docker-compose
### Why
You have a working local environment, however you already need to chain a few commands, and as your app will be growing more complex, the setup will be harder to maintain.
Instead of having to type an _imperative_ chain of commands, you can have a _declarative_ description of your local _docker/container_ application. That's is why `docker-compose` is made for: it reads this config and run the right `docker commands` for you.
### What
We need to be able to launch the current container with only the `docker-compose up` command.
The `docker-compose.yaml` file will contains everything needed:
- how to build the image
- how to run the container, including configuration of port
- how to link it to another container
- how to persistent storage
### How
There is a [_get started_](https://docs.docker.com/compose/gettingstarted/) article, or the [complete specification](https://docs.docker.com/compose/compose-file/)
- define your guestbook service
- you can use the image you built, but you can also specify how to rebuild it!
- don't forget to expose the port needed for your application
### Checks
- [ ] I can launch locally the application with `docker-compose up`
- [ ] I can see the UI in my brower at `http://localhost:3000`
<details>
<summary>Compare your work to the solution before moving on. Are there differences? Is your approach better or worse? Why?</summary>
You should have something like:
```yaml
version:'3'
services:
guestbook:
build:
context:./
dockerfile:Dockerfile
ports:
-3000:3000
```
</details>
## 4. Add a database to your service
### Why
If you test your app, you can see a big **⚠️ No database connection... ⚠️**. Furthermore, when you try to add something to the guestbook, it hangs (⌛) without saving it (try to refresh the page).
The application is actually stateless, and needs a Redis backend to save its state. To avoid interfering with your local installation, we will run it in container, using once again `docker` and `docker-compose`.
### What
We simply need to add a new service in our docker-compose file, and have a way for the app to use it.
### How
1. Add a `redis` service in your app. Don't build redis locally, but use the public `redis:6` image.
2. Expose its redis port `6379`.
3. Make the guestbook app use it:
The Guestbook app uses _environment variable_ for its configuration. Here you need to setup the `REDIS_HOST` variable to the hostname of your redis cluster. In a docker-compose environment, each service can be called with its name.
4. Try to run it: does the application store the state?
5. (Optional) Make it persistent!
Currently, if you save some sentences in the app, then run `docker-compose down` and `docker-compose up` again, you'll see that you will loose all your data! 😢
You can manage volumes in docker-compose, which are persisted, and mount these volumes in your app. If you prefer, you can also link a local folder to a container, it can be useful for live reloading.
### Check
- [ ] The application actually saves messages

- [ ] (Optional) If you run `docker-compose down`, you don't loose data when you relaunch the app.
<details>
<summary><em>Compare your work to the solution before moving on. Are there differences? Is your approach better or worse? Why?</em></summary>
You can find the complete solution [here](https://github.com/padok-team/dojo-guestbook/blob/feat/solution/docker-compose.yml). Don't spoil yourself too much!
</details>
## 5. Deploy you app on Kubernetes: the Pod
> If you are here, ask for a quick formation on Kubernetes. We will make a quick overview for everyone!