Docker on Windows

Docker on Windows

[:es]Es importante hacer la diferencia entre Docker for Windows y Docker on Windows.

  • Docker for Windows permite usar contenedores Linux en una máquina Windows 10.
  • Docker on Windows permite usar contenedores Windows en una máquina Windows Server 2016.

En este post, voy a describir cómo se puede usar Docker on Windows

Instalar Docker

Los contenedores Windows funcionan únicamente en Windows Server 2016, para hacer pruebas se puede conseguir una versión de evaluación de 180 días en la web de Microsoft.

Antes de activar el servicio docker, se necesita descargar las últimas actualizaciones de Windows
2016, luego en PowerShell (y siendo administrador), entrar los comandos siguientes :

Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
Install-Module -Name DockerMsftProvider -Force
Install-Package -Name docker -ProviderName DockerMsftProvider -Force
Restart-Computer -Force

(Windows también tiene su package manager ahora)

Como el último comando lo deja suponer, se necesitará reiniciar la máquina.

Esto instala el servicio docker (docker daemon) y el cliente docker.

Descargar las imágenes de base

Microsoft provee dos imágenes que se puedan usar como base para un contenedor Windows:

  • microsoft/windowsservercore
  • microsoft/nanoserver

La primera lleva un servidor Windows con todas sus funcionalidades, la segunda es una versión mucho más liviana, y lógicamente con menos funcionalidades.

La versión completa ocupa casi 10 Gb, así que dura bastante la descarga.

docker pull microsoft/windowsservercore

La versión NanoServer ocupa casi 1 Gb, lo que la hace mucho más conveniente para trasladarla o para un uso en el cloud por ejemplo, así que cuando se puede se debería preferir esa.

docker pull microsoft/nanoserver

(Acá se puede ver que docker se puede levantar/bajar usando net start/stop como cualquier servicio, y que el cliente docker se puede usar como en Linux)

Construir una imagen con una máquina virtual Java

Para construir una primera imagen vamos a descargar una JDK y agregarla en una imagen de base NanoServer.

En una carpeta dedicada, se copia la JDK y se crea un archivo Dockerfile (sin extensión), con el contenido siguiente:

FROM microsoft/nanoserver

MAINTAINER Guillaume Mayer <gmayer@arkho.tech>

SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop';"]

COPY jdk1.8.0_112 c:/jdk1.8.0_112

ENV JAVA_HOME c:\\jdk1.8.0_112

RUN $newPath = ('{0};{1}\bin' -f $env:PATH, $env:JAVA_HOME); \
 Write-Host ('Updating PATH: {0}' -f $newPath); \
 setx /M PATH $newPath;

  • Esa imagen ocupa la de Microsoft NanoServer como base
  • Se define PowerShell como shell por defecto
  • Se copia la JDK en la raíz (C:)
  • Se define la variable de entorno JAVA_HOME
  • Se agrega JAVA_HOME\bin al PATH

Hay que ser cuidadoso con las rutas con anti-slash (\) porque también es el carácter de escape de los dockerfiles, por eso se deben doblar.

Para construir esta imagen, se puede llamar el comando siguiente (parado en la carpeta que lleva el Dockerfile):

docker build -t java:8-nanoserver .

La option -t permite dar un nombre (tag) a la imagen.

Se puede probar la imagen con el comando siguiente:

docker run -it --rm java:8-nanoserver

(Las opciones -it permiten simular un terminal interactivo, la opción –rm le dice a docker de borrar el contenedor cuando
no se usa mas)

(Se puede ver que la carpeta del JDK fue copiada en la raiz de C: , que la variable de entorno apunta correctamente y que java funciona)

 Construir una imagen con una aplicación

Ahora que tenemos una imagen con Java funcionando, vamos crear otra que haga algo, un hello world en Spring Boot por ejemplo.

En este caso, es una aplicación que funciona con Jetty en modo standalone, y que responde “Hello, world!” cuando se llama al URL http://localhost:8080/hello/world

src/main/java/hello/HelloController.java :

package hello;

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.PathVariable;

@RestController
public class HelloController {

  @RequestMapping("/hello/{name}")
  String hello(@PathVariable String name) {
  return "Hello, " + name + "!";
  }
}

src/main/java/hello/Application.java :

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(applicationClass, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(applicationClass);
    }

    private static Class applicationClass = Application.class;
}

Archivo pom.xml :

<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>hello-world</artifactId>
<version>0.1.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<version>1.4.3.RELEASE</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

En otra carpeta dedicada, copiamos el jar y creemos otro Dockerfile con este contenido:


FROM java:8-nanoserver

MAINTAINER Guillaume Mayer <gmayer@arkho.tech>

COPY hello-world-0.1.0.jar c:/hello-world-0.1.0.jar

EXPOSE 8080

ENTRYPOINT java -jar hello-world-0.1.0.jar
  • Esa imagen ocupa la imagen con Java 8 previamente construida
  • Se copia el archivo JAR
  • Se expone el puerto 8080 (donde corre la app Spring Boot por defecto)
  • Se define el comando que se ejecutara al arrancar el contenedor (levantar la app Spring Boot)

Para construir la imagen :

docker build -t hello .

Para arrancar el contenedor :

docker run --name hello -d -p 8080:8080 hello

Acceder al contenedor

En Windows, los contenedores corren por defecto en un network de tipo “nat”.

Cada uno tiene su IP y no se puede acceder al contenedor directamente usando localhost como en Linux.

Para averiguar la dirección IP del contenedor se debe usar el comando docker inspect:

docker inspect hello

o

docker inspect -f {{.NetworkSettings.Networks.nat.IPAddress}} hello

Y se puede comprobar que la aplicación funciona :

 
twitterFacebooklinkedin
 

[:]