---
title: Curso Docker
tags: training, curso, docker, containers
description: Curso de Docker para principiates de la gerencia SCeI
---
# Práctica del curso de Docker
1. Crear cuenta docker hub
2. Constuir imagen docker basica
3. Construir imagen docker con aplicación
4. Construir solucion con docker-compose
5. Constuir imagen corporativa java
6. Subir imágenes docker a docker hub
7. Ejecutar contenedor
8. Destruir contenedor
9. Seguritizar imagen docker
10. Escanear imagen docker
11. Publicar imagen docker en docker privado
## Nivel 0 - Introducción
### Creacion de cuenta en docker hub
Docker Hub es el docker registry publico administrado por Docker, Inc. Si no cuentan con cuenta deben ingresar en el siquiente enlace https://hub.docker.com/signup
Para trabajar con docker se usan los siquientes comandos básicos:
`docker login` :arrow_right: login a docker registry o docker hub
`docker logs idcontenedor` :arrow_right: verificar logs del contenedor
`docker image ls` :arrow_right: listas imágenes
`docker ps` :arrow_right: container en ejecución
`docker inspect idcontenedor` :arrow_right: te muestra debug o metadata del contenedor
`docker build -t nombre-imagen .` :arrow_right: construcción de imágenes docker, se debe estan en mismo directorio donde se almacena el Dockerfile. A veces es necesario limpiar el caché del build , para ello se usa `--no-cache`
`docker tag nombre-imagen nombre-imagen:latest` :arrow_right: tagging de imágenes docker
`docker push (dockerhub-id) or docker-registry/nombre-imagen` :arrow_right: subida de imágenes docker a docker registry privado o a perfil personal de docker hub
`docker pull (dockerhub-id) or docker-registry/nombre-imagen` :arrow_right: descarga de docker imagen desde docker registry o docker hub
`docker exec` :arrow_right: ejecutar comandos o ingresar en un contenedor que se esta ejecutando
`docker run` opciones: `-ti` de forma interactiva , `-d` en background, `-p` puertos mapping/publish, `-v` volumen persistente, `--rm` elimina el contenedor cuando este tiene un crash
`docker stop/start/restart/top containerid` :arrow_right: ejecución de procesos de estatus sobre el contenedor docker.
Para contruir una imagen docker se emplean archivos Dockerfile, y estos contienen las siguientes llaves:
- `FROM`: cada Dockerfile comienza con FROM, con la introducción de compilaciones de varias etapas a partir de la versión 17.05, puede tener más de una instrucción FROM en un Dockerfile.
- `ENV`: Se setean variables de entorno.
- `RUN`: Se especifican los comandos de ejecución.
- `VOLUME`: se epescifica el volumen del host a consumir
- `COPY` vs `ADD`: Cumplen las mismas funciones, solo que ADD tiene más beneficios, pero lo principal es copiar archivos desde host hacia el contenedor.
- `USER`: Usuario que se encargara de ejecutar las instrucciones (`RUN`, `CMD`, `ENTRYPOINT`, `COPY`, `ADD`).
- `WORKDIR`: se usa para declarar directorio de trabajo, donde se almacena las aplicaciones o configuraciones.
- `EXPOSE`: Se usa para declarar los puertos que se van a exponer.
- `CMD` and `ENTRYPOINT`: `CMD` es la instrucción para especificar qué componente debe ejecutar su imagen con argumentos de la siguiente forma: `CMD ["ejecutable", "param1", "param2" ...]`, mientras que `ENTRYPOINT`, es es el principal ejecutable de tu imagen. En este caso, todo lo que especifique en `CMD` se agregará a `ENTRYPOINT` como parámetros.
## Nivel 1 - Construcción de imagen docker básica:
imagen basica con nmap, curl , vim
```dockerfile=
FROM alpine:latest
RUN apk update
RUN apk add curl nmap vim
```
Otra alternativa es:
```dockerfile=
FROM alpine:3.4
RUN apk update && \
apk add curl && \
apk add vim && \
apk add nmap
```
Se preguntarán ¿Por qué? Resulta que cada linea es una capa y mientra menos capas, más óptima la construcción y la descarga de la imagen docker.
Tenga en cuenta que solo las instrucciones `RUN`, `COPY` y `ADD` crean capas.
Por favor coloque los ejemplos de Dockerfile en carpetas distintas de su workspace
## Nivel 2 - Construir imagen docker con aplicación:
construya la siquiente imagen de aplicacion en python:
```dockerfile=
# Use an official Python runtime as a parent image
FROM python:2.7-slim
# Set the working directory to /app
WORKDIR /app
# Copy the current directory contents into the container at /app
COPY . /app
# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# Make port 80 available to the world outside this container
EXPOSE 80
# Define environment variable
ENV NAME World
# Run app.py when the container launches
CMD ["python", "app.py"]
```
Debes tener el archivo `requirements.txt` con el siquiente contenido:
```=
Flask
Redis
```
el ejemplo de la app es la siquiente:
```python=
from flask import Flask
from redis import Redis, RedisError
import os
import socket
# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
app = Flask(__name__)
@app.route("/")
def hello():
try:
visits = redis.incr("counter")
except RedisError:
visits = "<i>cannot connect to Redis, counter disabled</i>"
html = "<h3>Hello {name}!</h3>" \
"<b>Hostname:</b> {hostname}<br/>" \
"<b>Visits:</b> {visits}"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
```
Para constuir la ejecutar se debe ejecutar:
`docker build --tag=securityapp .`
Otras reglas de oro al momento de construir imágenes docker son las siquientes:
- Los Containers debe ser efímeros, no tienen vida útil, son recreados, reemplazados, destruidos automáticamente, por lo que cualquier _"estado"_ debe estar fuera de los container.
- Cada contenedor debe tener su función, nunca usar _"monolítos"_
## Nivel 3 - Construir solución con `docker-compose`:
En este nivel para cumplir con esa regla de oro, que establece que ningún contenedor debe tener mas de 2 servicios corriendo, existe `docker-compose`, a continuacion copiaremos el `dockerfile` , la app y el archivo `requirement.txt` en una nueva carpeta de trabajo y agregaremos un nuevo archivo con el nombre `docker-compose.yml`, dicho archivo tiene el siquiente contenido:
:::info
:eyes: OJO, antes valide si tiene docker-compose, si no lo tiene instale de la siquiente forma:
```sh
curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
```
:::
A continuación agregue el archivo `docker-compose.yml` a su carpeta de trabajo
```yaml=
version: '2'
services:
web:
build: .
ports:
- "80:80"
volumes:
- .:/app
depends_on:
- redis
redis:
image: redis
```
A continuación ejecutamos el comando:
```docker-compose up -d```
Finalmente observaremos dos servicios ejecutados en distintos contenedores, interconectados, usando un volumen compartido para subir o modificar el código sin re-construir la imagen.
## Nivel 4 - Constuir imagen corporativa java:
Construya una imagen corporativa de su lenguaje de preferencia, ejemplo java
Para ello utilizaremos el siguiente `Dockerfile`:
```dockerfile=
FROM alpine:latest
ENV LANG es_ES.UTF-8
ENV LANGUAGE es_ES:es
ENV LC_ALL es_ES.UTF-8
RUN apk upgrade --no-cache
RUN adduser -h /app -D java
RUN apk add --no-cache --update-cache wget curl openssl bash
RUN apk --no-cache add ca-certificates wget && \
wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub && \
wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.29-r0/glibc-2.29-r0.apk && \
wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.29-r0/glibc-bin-2.29-r0.apk && \
wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.29-r0/glibc-i18n-2.29-r0.apk && \
apk add glibc-2.29-r0.apk glibc-bin-2.29-r0.apk glibc-i18n-2.29-r0.apk
RUN /usr/glibc-compat/bin/localedef -i es_ES -f UTF-8 es_ES.UTF-8
RUN ZULU_ARCH=zulu8.36.0.1-ca-jdk8.0.202-linux_x64.tar.gz && \
INSTALL_DIR=/usr/lib/jvm && \
BIN_DIR=/usr/bin && \
MAN_DIR=/usr/share/man/man1 && \
ZULU_DIR=$( basename ${ZULU_ARCH} .tar.gz ) && \
apk update && \
apk add --no-cache ca-certificates wget && \
update-ca-certificates && \
wget -q https://cdn.azul.com/zulu/bin/${ZULU_ARCH} && \
mkdir -p ${INSTALL_DIR} && \
tar -xf ./${ZULU_ARCH} -C /usr/lib/jvm/ && rm -f ${ZULU_ARCH} && \
cd ${BIN_DIR}; find ${INSTALL_DIR}/${ZULU_DIR}/bin -type f -perm -a=x -exec ln -s {} . \; && \
mkdir -p ${MAN_DIR} && \
cd ${MAN_DIR}; find ${INSTALL_DIR}/${ZULU_DIR}/man/man1 -type f -name "*.1" -exec ln -s {} . \; && \
java -version
COPY . /app
RUN rm *.apk
COPY hardening.sh .
RUN ["chmod", "+x", "hardening.sh"]
RUN ["./hardening.sh"]
RUN rm hardening.sh
CMD [""]
USER java
EXPOSE 8080
WORKDIR /app
ENV JAVA_OPTS="-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app/app.jar" ]
```
Descargue el archivo `hardening.sh` y `app.jar` , desde el repositorio, http://vulnapp.buffetcloud.io/hardening.sh y http://vulnapp.buffetcloud.io/app.jar
Construya la imagen con el comando:
`docker build -t NOMBREIMAGEN .`
Recuerde que debe estar dentro del directorio nivel4.
.- Para validar la creacion de las imágenes ejecute el siquiente comando:
docker image ls
.- Para subir imagen docker a docker hub/docker registry, se debe estar autenticado
docker hub:
ejecutando docker login, y colocando los credenciales de su docker hub
docker registry:
docker login -u username registrysecaas -p password registrysecaas.azurecr.io
.- Una vez autenticado se procede a tagear la imagen, para darle el nombre como se almacenara en el registry
docker tag securityapp nombredockerhub/securityapp
.- A continuacion se procede a subir la imagen
docker push nombredockerhub/securityapp
Si visita su perfil de docker hub puede verificar la creacion de la imagen docker
suba tambien la imagen al docker registry de secaas, de la siquiente manera:
tagguiando la imagen como registrysecaas.azurecr.io/practicas/NOMBRE:latest
pusheando la imagen con : docker push registrysecaas.azurecr.io/practicas/NOMBRE:latest
.- Escanear imagen docker para verificar vulnerabilidades con el siquiente comando:
./drone-clair --url http://clair.buffetcloud.io:6060 --username xxxx --password cccccc --scan_image registrysecaas.azurecr.io/practicas/NOMBRE:latest
.- Para ejecutar un contenedor tenemos las siquientes opciones:
la estructura del comando es el siquiente: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
podemos ejecutar imágenes custom creadas o buscarlas mediante el comando docker search xxxxx y descargarlas con docker pull
algunos ejemplos a ejecutar:
docker run -it ubuntu
docker run --name my-redis -d redis
docker run -d -p 6379:6379 --name redis redis
docker run -d -p 6379:6379 --name redis -v /tmp/data:/data redis >>>> Si queremos agregar un volumen a un contenedor, ejecutamos el siquiente comando:
docker run -d -t -i -e NAMESPACE='staging' -e PASSWORD='foo' busybox sh
.- Para debuggear un contenedor, podemos ejecutar lo siquiente:
docker ps -a
docker logs IDCONTENEDOR
docker inspect IDCONTENEDOR
.- Para modificar una imagen ya creada, podemos entrar al contenedor, realizar los cambios, y a continuación ejecutar el comando: docker commit IDCONTAINER nuevonombreimagen
.- Para exportar una imagen docker desde una maquina a otra, se usa docker save NOMBREDEIMAGEN > ARCHIVOIMAGEN.tar, luego en la maquina destino debes ejecutar docker load < ARCHIVOIMAGEN.tar
.- Cuando trabajamos con conexiones proxiadas hacia docker hub, usamos la siquiente configuración
en ubuntu:
editar el archivo /etc/default/docker y colocar las siquiente variables
HTTP_PROXY="http://<proxy_host>:<proxy_port>"
HTTPS_PROXY="http://<proxy_host>:<proxy_port>"
Si son proxy corporativos, pueden agregar username y password, de la siquiente forma: HTTP_PROXY="http://<username>:<password>@<proxy_host>:<proxy_port>"
.- Para destruir un contenedor/imagen ejecutamos los siquientes comandos:
docker rm IDCONTENEDOR/NOMBRECONTENEDOR, se debe detener el contenedor antes
docker rmi IDIMAGEN/NOMBRECONTENEDOR
Para eliminar de forma masiva:
docker system prune -a >>>>> eliminacion interactiva
docker images purge >>> eliminacion incluyendo layer usados por contenedores que son ejecutados u otra imagen docker taggeada
docker rm $(docker ps -a -f status=exited -q) >>> eliminacion de contenedores con status exited