Publi

Contenedores docker de aplicaciones de escritorio [con ejemplos listos para usar]

En los últimos años, han surgido varias tecnologías para compartimentar aplicaciones. Desde ejecutar una aplicación desde una jaula, hasta virtualizar un sistema operativo completo dentro de nuestro sistema. Algo que se utiliza mucho en servidores. Actualmente hay una tecnología en auge, y es Docker. Docker nos permite encerrar una aplicación, junto con las bibliotecas que necesita para funcionar y ejecutarla en un entorno aislado, de forma que la aplicación compartimentada no pueda ver nada del exterior. Aunque, si lo deseamos, podrá conectar por red con otros recursos o a Internet.

Docker se usa mucho en el ámbito de los servidores. Nos permite ejecutar aplicaciones de manera que cada una se ejecute en un espacio determinado, con unos permisos, utilizando unos recursos de sistema definidos y utilizando un sistema operativo y bibliotecas de sistema determinadas, que pueden no ser las de nuestro sistema. Otra ventaja añadida es que los contenedores docker tienen un guión definido para su construcción, lo que nos asegura que la configuración del software del contenedor no variará entre instancias, podemos perfectamente configurarlo en nuestro ordenador local e instalarlo más tarde en un servidor cuando nos aseguremos de que todo esté bien, o hacer que todo un equipo (o empresa) utilice el mismo software, en la misma versión y con la misma configuración y evitar así la típica excusa de: «En mi ordenador funciona.»

Docker en el escritorio

Pero docker compartimenta aplicaciones, da igual del tipo que sean. Así que, ¿por qué no compartimentar aplicaciones de escritorio? De forma que encerremos todo lo que necesitamos de la misma en un entorno controlado y así evitamos tener problemas con dependencias, versiones, incluso con entornos Java cuando utilicemos dicha aplicación. Es muy común que una aplicación se actualice para utilizar bibliotecas en versiones muy nuevas y nuestra distribución no instale esas dependencias, incluso cuando las instalamos a mano, podemos meter la pata y hacer que nuestro sistema se vuelva inestable o que no funcione directamente. También es muy útil cuando necesitamos software que ya no está soportado, o necesitamos una versión antigua y vamos a ejecutarla en un sistema operativo más moderno que no instala las versiones de las bibliotecas que necesitamos.

Puede parecer un gasto innecesario de disco duro, porque seguramente tengamos muchas bibliotecas y software repetido en nuestro sistema; y de memoria, porque habrá veces que la misma biblioteca esté cargada en RAM varias veces (aunque podemos optimizar un poco nuestro kernel para que no tenga mucho problema con eso). Pero nos dará seguridad en nuestro sistema, ya que esta aplicación estará aislada y no tendrá acceso a recursos sin permiso, para esa aplicación ni existirán. Y además, la posibilidad de llevarnos la aplicación a otro equipo o servidor, construirla allí y que todo esté igual que en nuestro ordenador.

Construyendo el Dockerfile. Como ejemplo dbeaver

Cada Dockerfile será independiente de la aplicación que necesitemos ejecutar. Aunque podemos utilizar este archivo como base. En este caso, vamos a dockerizar el programa dbeaver, una poderosa herramienta de administración de base de datos con muchas opciones. Como vemos, es una aplicación hecha en Java. Y, puede que en nuestro ordenador o nuestro servidor, no nos interese tener un entorno de Java públicamente disponible, y solo dejarlo para este programa.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
FROM ubuntu:18.04
MAINTAINER Gaspar Fernandez <gaspar.fernandez@totaki.com>
ARG UNAME=user
ARG UID=1000
ARG GID=1000

RUN apt-get update \
        && apt-get -y install wget \
        && apt-get -y install libxext6 libxrender1 libxtst6 libxi6 software-properties-common \
        && apt-get -y install openjdk-8-jre \
        && wget -q --output-document=/tmp/dbeaver.deb https://dbeaver.io/files/dbeaver-ce_latest_amd64.deb \
        && dpkg -i /tmp/dbeaver.deb \
        && mkdir -p /home/$UNAME \
        && echo "$UNAME:x:${UID}:${GID}:Developer,,,:/home/$UNAME:/bin/bash" >> /etc/passwd \
        && echo "$UNAME:x:${UID}:" >> /etc/group \
        && chown ${UID}:${GID} -R /home/$UNAME \
        && gpasswd -a $UNAME audio \
        && gpasswd -a $UNAME video

COPY docker-entry.sh /usr/local/bin
ENTRYPOINT ["/usr/local/bin/docker-entry.sh"]

USER $UNAME
ENV HOME /home/$UNAME

En este caso, partimos de una versión 18.04 de Ubuntu, en la que tenemos que instalar dependencias del escritorio X y OpenDJK, seguidamente, descargar el .deb de la página oficial de dbeaver para proceder a la instalación del programa.
Seguidamente, aunque podemos continuar como root, sobre todo para los programas de escritorio, no es aconsejable y muchos de ellos se quejan al arrancar. Así que creamos un usuario (podríamos cambiarle el nombre al usuario, aunque no será tan importante, por ahora se llamará user). Luego le asignamos grupos (en este caso no es tan importante, pero hay programas con los que sí).

Generalmente, me gusta escribir un docker-entry.sh para cada aplicación a dockerizar, aunque solo sea para ejecutar el programa nada más. Porque, a veces tenemos que filtrar argumentos, ejecutar un pequeño script antes que el programa o hacer una copia de la configuración, etc. En este caso, nuestro docker-entry.sh será este:

1
2
3
4
#!/bin/bash

echo "Ejecutando dbeaver..."
dbeaver

Tras ello, aunque podríamos utilizar docker-compose, vamos a crear un script para construir el contenedor y otro para ejecutar la aplicación. Aunque la ejecución es más o menos sencilla, no tenemos por qué recordar los argumentos para ejecutar el programa. Además, así podremos crear más fácilmente accesos directos y otras utilidades derivadas.

build.sh

1
2
3
4
5
6
#!/bin/bash
readonly SCRIPTPATH="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"

pushd $SCRIPTPATH
docker build -t gasparfm/dbeaver .
popd

Primero, almacenamos en la variable SCRIPTPATH el nombre del directorio donde se encuentra el script, me gusta utilizar esta línea por si llamamos al script desde una ruta diferente a la ruta donde está, o si hacemos un enlace simbólico al archivo. De esta forma, obtendremos en la variable la ruta exacta donde se encuentra el archivo de script al que hace referencia el enlace. Y así podremos realizar tareas en ese directorio, como por ejemplo la construcción de la imagen de nuestro contenedor.

run.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
readonly SCRIPTPATH="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"

pushd $SCRIPTPATH
docker run --rm --name dbeaver -e DISPLAY=$DISPLAY \
    -v /dev/shm:/dev/shm:rw \
    -v /etc/machine-id:/etc/machine-id:ro \
    -v /var/lib/dbus:/var/lib/dbus:ro \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    -v /etc/localtime:/etc/localtime:ro \
    -v /etc/hosts:/etc/hosts:ro \
    -v $(pwd)/user:/home/user \
    -v $HOME/.ssh:/home/user/sshkeys:ro \
    gasparfm/dbeaver
popd

Para ejecutar el script, podemos crear un enlace en /usr/local/bin a este archivo, llamándolo como queramos. Aquí comento algunos argumentos utilizados:

  • –rm: Borra el contenedor si existe. Para utilizar siempre el mismo, porque normalmente solo vamos a necesitar una instancia en ejecución
  • –name dbeaver: Le damos nombre al contenedor, para que no tenga un nombre aleatorio.
  • -e DISPLAY=$DISPLAY: Asignamos la pantalla sobre la que escribirá la aplicación a la pantalla actual del usuario.
  • -v /dev/shm:/dev/shm:rw: Algunos programas necesitan acceso a este dispositivo de memoria compartida.
  • -v /etc/machine-id:/etc/machine-id:ro : Extraemos el identificador único de la máquina.
  • -v /var/lib/dbus:/var/lib/dbus:ro : En este directorio también suele almacenarse el identificador único de la máquina.
  • -v /tmp/.X11-unix:/tmp/.X11-unix : En este directorio suelen almacenarse los sockets para acceder al entorno X.
  • -v /etc/localtime:/etc/localtime:ro : Aquí reside la zona horaria de nuestro ordenador y la vinculamos con la de la aplicación dockerizada.
  • -v /etc/hosts:/etc/hosts:ro : Si tenemos hosts personalizados en /etc/hosts, nuestra aplicación debe poder acceder a ellos. O, al menos, resolverlos.
  • -v $(pwd)/user:/home/user : Vinculamos el directorio local user (tendremos que crearlo) con el /home/user de la aplicación. Sobre todo porque este programa introduce mucha información que debe ser salvada. Por ejemplo, se descarga drivers de base de datos o genera muchos archivos de configuración, etc.
  • -v $HOME/.ssh:/home/user/sshkeys:ro : Una opción de este programa es que nos permite acceder a bases de datos a través de túneles SSH. Por eso mismo, estaría bien poder acceder a nuestras claves privadas de SSH. Podríamos montar este volumen así o copiar las claves a mano al directorio user.
  • gasparfm/dbeaver : Es el nombre del contenedor

Solo tendremos que ejecutar el programa:

Dockerizando yed

Ahora vamos a incluir un segundo programa, yed. Un software para crear diagramas, hecho en Java también. Pero esta vez necesita del JRE de Oracle. Lo que ya nos da una pista de la potencia y necesidad de dockerizar este tipo de aplicaciones.

Vamos a seguir el mismo sistema, creamos Dockerfile, docker-entry.sh, build.sh y run.sh.

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
FROM ubuntu:18.04
MAINTAINER Gaspar Fernandez <gaspar.fernandez@totaki.com>
ARG VERSION=3.18.1.1
ARG UNAME=user
ARG UID=1000
ARG GID=1000

RUN apt-get update \
        && apt-get -y install wget \
        && apt-get -y install libxext6 libxrender1 libxtst6 libxi6 software-properties-common unzip
RUN add-apt-repository ppa:webupd8team/java \
        && apt-get update \
        && echo debconf shared/accepted-oracle-license-v1-1 select true | \
                debconf-set-selections \
        && echo debconf shared/accepted-oracle-license-v1-1 seen true | \
                debconf-set-selections \
        && apt-get -y install oracle-java8-installer
RUN wget -q --output-document=/tmp/yEd.zip https://www.yworks.com/resources/yed/demo/yEd-${VERSION}.zip \
        && unzip /tmp/yEd.zip -d /opt/ \
        && mkdir -p /home/$UNAME \
        && echo "$UNAME:x:${UID}:${GID}:Developer,,,:/home/$UNAME:/bin/bash" >> /etc/passwd \
        && echo "$UNAME:x:${UID}:" >> /etc/group \
        && chown ${UID}:${GID} -R /home/$UNAME \
        && gpasswd -a $UNAME audio \
        && gpasswd -a $UNAME video

COPY docker-entry.sh /usr/local/bin
ENTRYPOINT ["/usr/local/bin/docker-entry.sh"]

USER $UNAME
ENV HOME /home/$UNAME

docker-entry.sh:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash

ARGS=''
if [ $# -ge 1 ]; then
  ARGS="$HOME/$(basename $1)"
  shift 1
  ARGS="$@ $ARGS"
fi

java -jar "/opt/$(ls)/yed.jar" $ARGS

Esta vez, necesitamos que yed acepte argumentos del usuario, como por ejemplo el archivo que vamos a abrir.

build.sh

1
2
3
4
5
6
7
#!/bin/bash

readonly SCRIPTPATH="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"

pushd $SCRIPTPATH
docker build -t gasparfm/yed .
popd

run.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
readonly SCRIPTPATH="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"

pushd $SCRIPTPATH
docker run --rm --name yed -e DISPLAY=$DISPLAY \
    -v /dev/shm:/dev/shm:rw \
    -v /etc/machine-id:/etc/machine-id:ro \
    -v /var/lib/dbus:/var/lib/dbus:ro \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    -v /etc/localtime:/etc/localtime:ro \
    -v /etc/hosts:/etc/hosts:ro \
    -v $(pwd)/user:/home/user \
    gasparfm/yed
popd

Otras ideas: navegadores

De la misma forma podemos construir programas libres o privativos. Introduciendo una capa de seguridad en estos últimos, ya que no tendremos forma de saber lo que hacen. Incluso instalando versiones determinadas de wine para hacer funcionar algunos programas. Y como última idea, podemos dockerizar navegadores, instalaciones completas de Firefox / Chrome / Chromium en las que podremos limitar los recursos para no engullir RAM o CPU (podríamos limitarles la memoria a 1Gb, por ejemplo).

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
FROM ubuntu:18.04
ARG UNAME=user
ARG UID=1000
ARG GID=1000

RUN apt-get update \
    && apt-get -y install sudo iproute2 \
    && apt-get -y install chromium-browser \
    && apt-get -y clean
RUN mkdir -p /home/$UNAME && \
    echo "$UNAME:x:${UID}:${GID}:Developer,,,:/home/$UNAME:/bin/bash" >> /etc/passwd && \
    echo "$UNAME:x:${UID}:" >> /etc/group && \
    chown ${UID}:${GID} -R /home/$UNAME \
    && gpasswd -a $UNAME audio \
    && gpasswd -a $UNAME video \
    && echo "$UNAME ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/user

COPY docker-entry.sh /usr/local/bin
RUN chmod +x /usr/local/bin/docker-entry.sh
USER $UNAME
ENV HOME /home/$UNAME

ENTRYPOINT ["/usr/local/bin/docker-entry.sh"]

docker-entry.sh:

1
2
3
4
#!/bin/bash

# Podríamos incluir argumentos aquí.
chromium-browser

build.sh:

1
2
3
4
5
6
#!/bin/bash
readonly SCRIPTPATH="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"

pushd $SCRIPTPATH
docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) --build-arg UNAME=user -t gasparfm/chromium .
popd

run.sh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash

readonly SCRIPTPATH="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"

pushd $SCRIPTPATH

docker run -i --rm --name chromium --privileged -e DISPLAY=$DISPLAY \
    -v /dev/shm:/dev/shm:rw \
    -v /etc/machine-id:/etc/machine-id:ro \
    -v /run/user/$UID/pulse:/run/user/$UID/pulse:rw \
    -v /var/lib/dbus:/var/lib/dbus:ro \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    -v $(pwd)/user:/home/user \
    --device /dev/video0 \
    --memory="
1000m" \
    --cpus 1 \
    gasparfm/chromium
popd

En este caso, lo estamos limitando su procesamiento a 1 CPU (o núcleo) y aproximadamente 1Gb de memoria.

Aplicaciones dockerizadas

Y tú, ¿qué aplicación de escritorio dockerizarías?

Actualización 7/12/2018: Arreglado un fallo en la redacción cambiado Debian por Ubuntu, que se me fue la cabeza. Gracias a David Latorre por avisar.

Foto principal: unsplash-logoandrew jay

También podría interesarte....

There are 58 comments left Ir a comentario

  1. Osqui /
    Usando Mozilla Firefox Mozilla Firefox 63.0 en Fedora Linux Fedora Linux

    ¿Flatpak no persigue el mismo objetivo?

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 63.0 en Ubuntu Linux Ubuntu Linux

      Pues sí, es otro enfoque. Y el caso es que Flatpak, o Snap están por defecto en muchos sitios ya. Los paquetes que están disponibles son fáciles de instalar. Aunque me surgió el caso en el que no me era posible utilizar ninguno de los dos, pero sí pude utilizar Docker 🙂

  2. David Latorre /
    Usando Mozilla Firefox Mozilla Firefox 63.0 en Linux Linux

    Creo ver un pequeño error arriba y es el «Partimos de la versión 18.04 de Debian» esa es una versión de Ubuntu, en Debian vamos por la 9 😉

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 63.0 en Ubuntu Linux Ubuntu Linux

      ¡¡ Gracias David !! Arreglado, que se me fue la cabeza. ¡Saludos!

  3. felipe /
    Usando Mozilla Firefox Mozilla Firefox 60.0 en Linux Linux

    muy interesante, quiero probar esta manera para dockerizar una version de java, lo has hecho antes ?

  4. Ezequiel /
    Usando Google Chrome Google Chrome 75.0.3770.90 en Linux Linux

    Muy Interesante, ¿Existe la posibilidad de dockerizar por ejemplo Visual Studio 2019 Community y de esa forma evitar instalar una virtual machine solo para ese software?

    Desde ya gracias por tu tiempo.

  5. marcleo /
    Usando Mozilla Firefox Mozilla Firefox 71.0 en Ubuntu Linux Ubuntu Linux

    Hola
    Hice el ejemplo de Dbeaver y me tiró el siguiente error en el «docker run»

    marcelo@marcelo:~/proyectos/doc$ docker run –rm –name dbeaver -e DISPLAY=$DISPLAY \
    > -v /dev/shm:/dev/shm:rw \
    > -v /etc/machine-id:/etc/machine-id:ro \
    > -v /var/lib/dbus:/var/lib/dbus:ro \
    > -v /tmp/.X11-unix:/tmp/.X11-unix \
    > -v /etc/localtime:/etc/localtime:ro \
    > -v /etc/hosts:/etc/hosts:ro \
    > -v $(pwd)/user:/home/user \
    > -v $HOME/.ssh:/home/user/sshkeys:ro \
    > gasparfm/dbeaver
    Ejecutando dbeaver…
    > Start DBeaver Standalone [org.jkiss.dbeaver.core.application 6.3.2.202001051908]
    > Start Eclipse e4 Workbench [org.eclipse.e4.ui.workbench 1.11.0.v20191120-1917]
    > Start Eclipse Workbench Model [org.eclipse.e4.ui.model.workbench 2.1.600.v20191106-1503]
    > Start Eclipse Jobs Mechanism [org.eclipse.core.jobs 3.10.600.v20191122-2104]
    Dbeaver:
    An error has occurred. See the log file
    /home/marcelo/.eclipse/org.jkiss.dbeaver.product_6.g.2_620785679_linux_gtk_x86_64/configuration/1578512352444.log.

    Podrías ayudarme a ghacerlo andar? muchas gracias

  6. Fénix /
    Usando Google Chrome Google Chrome 87.0.4280.66 en Windows Windows NT

    El articulo es fantástico, a mi me gustaría Dokerizar una aplicación y un servicio. Tengo los instalables en *.msi y *.exe, la aplicación habla con el servicio por TCP :7171 y el servicio además habla por la red TCP: 1000 . Me recomiendas alguna lectura o algún consejo? mil gracias

    1. Gaspar Fernández / Post Author
      Usando Google Chrome Google Chrome 87.0.4280.88 en Linux Linux

      Gracias Fénix.
      Ha pasado tiempo desde tu comentario, no sé cómo van tus avances con él. No sé si hay alguna forma de dockerizar aplicaciones para Windows, sobre todo si tienen entorno gráfico, más allá de dockerizar un linux con wine y ejecutar las aplicaciones ahí. Y no sé si valen todas las aplicaciones. Hay personas que han dockerizado un IIS, o algún servicio de Windows, pero no te puedo decir más, lo siento.

  7. cbd sports cream /
    Usando Google Chrome Google Chrome 106.0.0.0 en Windows Windows NT

    This is wonderful and quite informative blog I have learnt so many things from here.
    cbd cream for sports

  8. Anthony Scotte /
    Usando Google Chrome Google Chrome 107.0.0.0 en Windows Windows NT

    Good blog along with the excellent quality stuff and I’m sure this will be greatly helpful. appliance repair mount vernon

  9. henoy /
    Usando Google Chrome Google Chrome 107.0.0.0 en Windows Windows NT

    Good tips! Las computadoras son muy inteligentes y nos ayudan mucho en el trabajo y el entretenimiento. Do you know stumble guys 2?

  10. Emely Homens /
    Usando Google Chrome Google Chrome 108.0.0.0 en Windows Windows NT

    Wow! Didn’t know there was so much to do here. Thanks for all the tips!!! Drywall Services Burnaby, BC

  11. geometry dash subzero /
    Usando Google Chrome Google Chrome 109.0.0.0 en Windows Windows NT

    But the most successful team is still one that has a high degree of flexibility. geometry dash subzero

  12. 바카라사이트 /
    Usando Google Chrome Google Chrome 109.0.0.0 en Windows Windows NT

    I have been looking for articles on these topics for a long time. 바카라사이트 I don’t know how grateful you are for posting on this topic. Thank you for the numerous articles on this site, I will subscribe to those links in my bookmarks and visit them often. Have a nice day.

  13. John Shaffer /
    Usando Google Chrome Google Chrome 109.0.0.0 en Windows Windows NT

    Gratitude For Sharing this Educational Post Shearling Area embraces the customer’s requirements with the best of administration!
    brown fringe leather jacket

  14. pahpah20 /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Aunque, si lo deseamos, podrá conectar por red con otros recursos o a Internet.
    Spades

  15. run 3 /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    This is something I’ve discussed with a friend. Run 3 is an addicting infinite runner game set in a futuristic universe in which you control a self-falling ball on a 3D track, combining old and new gameplay elements. A fast-paced game with some bizarre high-tech elements.

  16. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Actually, it’s pretty good to see! Tiler Adelaide

  17. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Thanks for sharing! Tiler Adelaide

  18. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Thanks for letting us know! Tiler Wollongong

  19. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Good to know about this! Tiling Wollongong

  20. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Excellent post! Concreters in Wollongong

  21. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Such a great post! Glenelg South

  22. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    I visited Your blog and got a massive number of informative articles. I read many articles carefully and got the information that I had been looking for for a long time. Hope you will write such a helpful article in future. Thanks for writing.Tilers in Hobart

  23. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Very useful and informative post! Tiling Townsville

  24. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Thats what I was looking for! Hallett Cove

  25. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Very informative post! tiler melbourne

  26. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    To be honest, I generally don’t read. But, this article caught my attention.seo adelaide

  27. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    I am really impressed with your writing style. Keep it up! Landscapers Canberra

  28. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Thanks for sharing! Sliding Doors Adelaide

  29. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    It’s so kind of you! Solar Panels Adelaide

  30. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Many many thanks to you! Cleaning Services Adelaide

  31. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Such beautiful writing this is. I appreciate your talent. Painters Adelaide

  32. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    You presented your ideas and thoughts really well on the paper. Solar Panels Adelaide

  33. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    What a great piece of article! seo adelaide

  34. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Very informative content. Thanks. tow truck wollongong

  35. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Please keep up the good work! drum lessons adelaide

  36. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Thanks for letting us know. Tiler Adelaide

  37. James Song /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Nice article, waiting for your anothe. Painters Adelaide

  38. poppy mizzi /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    Welcome to the game, fall guys will be a good way for you and your kids to have some downtime and relax. There are thousands of fun and interesting games for you to try.

  39. drywall contractor /
    Usando Google Chrome Google Chrome 111.0.0.0 en Windows Windows NT

    I’ve been wanting to learn more about Docker for a while now, and your tutorial made it super easy to get started. I love how you broke everything down into simple, easy-to-understand steps. Thanks for sharing your knowledge with us!

  40. Lesly /
    Usando Google Chrome Google Chrome 111.0.0.0 en Windows Windows NT

    This is one of the good articles you can find on the net explaining everything in detail regarding the topic. I thank you for taking the time to share your thoughts and ideas to a lot of readers out there. Share this also, https://www.tejadosygoterasalbacete.com/construccion-de-tejados-y-cubiertas

  41. sfsd /
    Usando Google Chrome Google Chrome 112.0.0.0 en Windows Windows NT

    I am very impressed with your article. Like you said when you delete a result, run 3 it will be pushed from the left participant column to the right result column. I like it very much.

  42. 토토사이트먹튀검증 /
    Usando Google Chrome Google Chrome 112.0.0.0 en Windows Windows NT

    This is the perfect post.토토사이트먹튀검증 It helped me a lot. If you have time, I hope you come to my site and share your opinions. Have a nice day.

  43. blabling2 /
    Usando Google Chrome Google Chrome 113.0.0.0 en Windows Windows NT

    good post. and connect 4 good game

  44. Mike Rooney /
    Usando Google Chrome Google Chrome 113.0.0.0 en Windows Windows NT

    This is a great inspiring article. I am pretty much pleased with your good work. You put really very helpful information. Robert Maaser Blood & Gold Coat

  45. Louisel /
    Usando Google Chrome Google Chrome 113.0.0.0 en Windows Windows NT

    This is a good post. This post gives truly quality information. I’m going to look into it! Timber fencing experts

  46. Tom devid /
    Usando Google Chrome Google Chrome 113.0.0.0 en Windows Windows NT

    This was truly an intriguing point and I kinda concur with what you have specified here! Snakeskin Jacket

  47. OkSports /
    Usando Google Chrome Google Chrome 113.0.0.0 en Windows Windows NT

    There’s no doubt i would fully rate it after i read what is the idea about this article. You did a nice job.
    Ok Sports

  48. eladó vállalkozás hirdetése /
    Usando Google Chrome Google Chrome 112.0.0.0 en Windows Windows NT

    Well written website, and it is a helpful content,. thanks.

  49. OKBet /
    Usando Google Chrome Google Chrome 114.0.0.0 en Windows Windows NT

    Excellent to the point article and news.. Well appreciated, My sites: OKBet FAQ

  50. Kenny /
    Usando Google Chrome Google Chrome 114.0.0.0 en Windows Windows NT

    Its benefits extend beyond servers, and Docker is also used extensively for local development and testing, enabling developers to work in consistent environments and simplify the deployment process. But how to repair drywall?

  51. play snake /
    Usando Google Chrome Google Chrome 114.0.0.0 en Windows Windows NT

    It’s such a pleasure to find somebody online who genuinely knows what they’re talking about, if you don’t mind me saying so right now. You actually acquire the ability to bring a concern to light and make it a significant issue. It would be great if a lot more people read this and understood and appreciated this aspect of your tale. I find it hard to comprehend that you haven’t gained greater popularity given that you unquestionably offer the gift.

  52. Gassy /
    Usando Google Chrome Google Chrome 115.0.0.0 en Windows Windows NT

    You really develop the capacity to bring an issue to light and elevate its significance to a level that warrants attention. It would be wonderful if a significant number of more individuals read this and understood and appreciated this facet of your story. @snow rider 3d

  53. 먹튀사이트 /
    Usando Google Chrome Google Chrome 115.0.0.0 en Windows Windows NT

    Thank you for sharing good information. I really like your content. I have also provided you with great information. Please visit my website.먹튀사이트 Your way of telling this paragraph is actually very good, and everyone can easily understand it. Thank you very much.

  54. larry larryellison /
    Usando Google Chrome Google Chrome 115.0.0.0 en Windows Windows NT

    Docker geometry dash bloodbath is really a great application. Worth a try and experience

  55. monkey mart /
    Usando Google Chrome Google Chrome 116.0.0.0 en Windows Windows NT

    While I was browsing the internet, I luckily discovered this website and discovered a lot of pretty interesting content here. Reading it is a very enjoyable experience. It was very enjoyable for me. Many thanks for making all of this information available to us.

Leave a Reply to Emely Homens Cancle Reply