--- 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