cker Inc. packages vs distribution packages * Docker Inc. releases new versions monthly (edge) and quarterly (stable) * Releases are immediately available on Docker Inc.'s package repositories * Linux distros don't always update to the latest Docker version (Sometimes, updating would break their guidelines for major/minor upgrades) * Sometimes, some distros have carried packages with custom patches * Sometimes, these patches added critical security bugs ☹ * Installing through Docker Inc.'s repositories is a bit of extra work … … but it is generally worth it! .debug[[containers/Installing_Docker.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Installing_Docker.md)] --- ## Installing Docker on macOS and Windows * On macOS, the recommended method is to use Docker Desktop for Mac: https://docs.docker.com/docker-for-mac/install/ * On Windows 10 Pro, Enterprise, and Education, you can use Docker Desktop for Windows: https://docs.docker.com/docker-for-windows/install/ * On older versions of Windows, you can use the Docker Toolbox: https://docs.docker.com/toolbox/toolbox_install_windows/ * On Windows Server 2016, you can also install the native engine: https://docs.docker.com/install/windows/docker-ee/ .debug[[containers/Installing_Docker.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Installing_Docker.md)] --- ## Docker Desktop * Special Docker edition available for Mac and Windows * Integrates well with the host OS: * installed like normal user applications on the host * provides user-friendly GUI to edit Docker configuration and settings * Only support running one Docker VM at a time ... ... but we can use `docker-machine`, the Docker Toolbox, VirtualBox, etc. to get a cluster. .debug[[containers/Installing_Docker.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Installing_Docker.md)] --- class: extra-details ## Docker Desktop internals * Leverages the host OS virtualization subsystem (e.g. the [Hypervisor API](https://developer.apple.com/documentation/hypervisor) on macOS) * Under the hood, runs a tiny VM (transparent to our daily use) * Accesses network resources like normal applications (and therefore, plays better with enterprise VPNs and firewalls) * Supports filesystem sharing through volumes (we'll talk about this later) .debug[[containers/Installing_Docker.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Installing_Docker.md)] --- ## Running Docker on macOS and Windows When you execute `docker version` from the terminal: * the CLI connects to the Docker Engine over a standard socket, * the Docker Engine is, in fact, running in a VM, * ... but the CLI doesn't know or care about that, * the CLI sends a request using the REST API, * the Docker Engine in the VM processes the request, * the CLI gets the response and displays it to you. All communication with the Docker Engine happens over the API. This will also allow to use remote Engines exactly as if they were local. .debug[[containers/Installing_Docker.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Installing_Docker.md)] --- ## Important PSA about security * If you have access to the Docker control socket, you can take over the machine (Because you can run containers that will access the machine's resources) * Therefore, on Linux machines, the `docker` user is equivalent to `root` * You should restrict access to it like you would protect `root` * By default, the Docker control socket belongs to the `docker` group * You can add trusted users to the `docker` group * Otherwise, you will have to prefix every `docker` command with `sudo`, e.g.: ```bash sudo docker version ``` .debug[[containers/Installing_Docker.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Installing_Docker.md)] --- class: pic .interstitial[![Image separating from the next module](https://gallant-turing-d0d520.netlify.com/containers/Container-Ship-Freighter-Navigation-Elbe-Romance-1782991.jpg)] --- name: toc-docker-engine-and-other-container-engines class: title Docker Engine and other container engines .nav[ [Previous section](#toc-installing-docker) | [Back to table of contents](#toc-module-7) | [Next section](#toc-init-systems-and-pid-) ] .debug[(automatically generated title slide)] --- # Docker Engine and other container engines * We are going to cover the architecture of the Docker Engine. * We will also present other container engines. .debug[[containers/Container_Engines.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Container_Engines.md)] --- class: pic ## Docker Engine external architecture ![](images/docker-engine-architecture.svg) .debug[[containers/Container_Engines.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Container_Engines.md)] --- ## Docker Engine external architecture * The Engine is a daemon (service running in the background). * All interaction is done through a REST API exposed over a socket. * On Linux, the default socket is a UNIX socket: `/var/run/docker.sock`. * We can also use a TCP socket, with optional mutual TLS authentication. * The `docker` CLI communicates with the Engine over the socket. Note: strictly speaking, the Docker API is not fully REST. Some operations (e.g. dealing with interactive containers and log streaming) don't fit the REST model. .debug[[containers/Container_Engines.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Container_Engines.md)] --- class: pic ## Docker Engine internal architecture ![](images/dockerd-and-containerd.png) .debug[[containers/Container_Engines.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Container_Engines.md)] --- ## Docker Engine internal architecture * Up to Docker 1.10: the Docker Engine is one single monolithic binary. * Starting with Docker 1.11, the Engine is split into multiple parts: - `dockerd` (REST API, auth, networking, storage) - `containerd` (container lifecycle, controlled over a gRPC API) - `containerd-shim` (per-container; does almost nothing but allows to restart the Engine without restarting the containers) - `runc` (per-container; does the actual heavy lifting to start the container) * Some features (like image and snapshot management) are progressively being pushed from `dockerd` to `containerd`. For more details, check [this short presentation by Phil Estes](https://www.slideshare.net/PhilEstes/diving-through-the-layers-investigating-runc-containerd-and-the-docker-engine-architecture). .debug[[containers/Container_Engines.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Container_Engines.md)] --- ## Other container engines The following list is not exhaustive. Furthermore, we limited the scope to Linux containers. We can also find containers (or things that look like containers) on other platforms like Windows, macOS, Solaris, FreeBSD ... .debug[[containers/Container_Engines.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Container_Engines.md)] --- ## LXC * The venerable ancestor (first released in 2008). * Docker initially relied on it to execute containers. * No daemon; no central API. * Each container is managed by a `lxc-start` process. * Each `lxc-start` process exposes a custom API over a local UNIX socket, allowing to interact with the container. * No notion of image (container filesystems have to be managed manually). * Networking has to be set up manually. .debug[[containers/Container_Engines.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Container_Engines.md)] --- ## LXD * Re-uses LXC code (through liblxc). * Builds on top of LXC to offer a more modern experience. * Daemon exposing a REST API. * Can manage images, snapshots, migrations, networking, storage. * "offers a user experience similar to virtual machines but using Linux containers instead." .debug[[containers/Container_Engines.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Container_Engines.md)] --- ## CRI-O * Designed to be used with Kubernetes as a simple, basic runtime. * Compares to `containerd`. * Daemon exposing a gRPC interface. * Controlled using the CRI API (Container Runtime Interface defined by Kubernetes). * Needs an underlying OCI runtime (e.g. runc). * Handles storage, images, networking (through CNI plugins). We're not aware of anyone using it directly (i.e. outside of Kubernetes). .debug[[containers/Container_Engines.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Container_Engines.md)] --- ## systemd * "init" system (PID 1) in most modern Linux distributions. * Offers tools like `systemd-nspawn` and `machinectl` to manage containers. * `systemd-nspawn` is "In many ways it is similar to chroot(1), but more powerful". * `machinectl` can interact with VMs and containers managed by systemd. * Exposes a DBUS API. * Basic image support (tar archives and raw disk images). * Network has to be set up manually. .debug[[containers/Container_Engines.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Container_Engines.md)] --- ## Kata containers * OCI-compliant runtime. * Fusion of two projects: Intel Clear Containers and Hyper runV. * Run each container in a lightweight virtual machine. * Requires running on bare metal *or* with nested virtualization. .debug[[containers/Container_Engines.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Container_Engines.md)] --- ## gVisor * OCI-compliant runtime. * Implements a subset of the Linux kernel system calls. * Written in go, uses a smaller subset of system calls. * Can be heavily sandboxed. * Can run in two modes: * KVM (requires bare metal or nested virtualization), * ptrace (no requirement, but slower). .debug[[containers/Container_Engines.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Container_Engines.md)] --- ## Overall ... * The Docker Engine is very developer-centric: - easy to install - easy to use - no manual setup - first-class image build and transfer * As a result, it is a fantastic tool in development environments. * On servers: - Docker is a good default choice - If you use Kubernetes, the engine doesn't matter .debug[[containers/Container_Engines.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Container_Engines.md)] --- class: pic .interstitial[![Image separating from the next module](https://gallant-turing-d0d520.netlify.com/containers/ShippingContainerSFBay.jpg)] --- name: toc-init-systems-and-pid- class: title Init systems and PID 1 .nav[ [Previous section](#toc-docker-engine-and-other-container-engines) | [Back to table of contents](#toc-module-7) | [Next section](#toc-advanced-dockerfile-syntax) ] .debug[(automatically generated title slide)] --- # Init systems and PID 1 In this chapter, we will consider: - the role of PID 1 in the world of Docker, - how to avoid some common pitfalls due to the misuse of init systems. .debug[[containers/Init_Systems.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Init_Systems.md)] --- ## What's an init system? - On UNIX, the "init system" (or "init" in short) is PID 1. - It is the first process started by the kernel when the system starts. - It has multiple responsibilities: - start every other process on the machine, - reap orphaned zombie processes. .debug[[containers/Init_Systems.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Init_Systems.md)] --- class: extra-details ## Orphaned zombie processes ?!? - When a process exits (or "dies"), it becomes a "zombie". (Zombie processes show up in `ps` or `top` with the status code `Z`.) - Its parent process must *reap* the zombie process. (This is done by calling `waitpid()` to retrieve the process' exit status.) - When a process exits, if it has child processes, these processes are "orphaned." - They are then re-parented to PID 1, init. - Init therefore needs to take care of these orphaned processes when they exit. .debug[[containers/Init_Systems.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Init_Systems.md)] --- ## Don't use init systems in containers - It's often tempting to use an init system or a process manager. (Examples: *systemd*, *supervisord*...) - Our containers are then called "system containers". (By contrast with "application containers".) - "System containers" are similar to lightweight virtual machines. - They have multiple downsides: - when starting multiple processes, their logs get mixed on stdout, - if the application process dies, the container engine doesn't see it. - Overall, they make it harder to operate troubleshoot containerized apps. .debug[[containers/Init_Systems.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Init_Systems.md)] --- ## Exceptions and workarounds - Sometimes, it's convenient to run a real init system like *systemd*. (Example: a CI system whose goal is precisely to test an init script or unit file.) - If we need to run multiple processes: can we use multiple containers? (Example: [this Compose file](https://github.com/jpetazzo/container.training/blob/master/compose/simple-k8s-control-plane/docker-compose.yaml) runs multiple processes together.) - When deploying with Kubernetes: - a container belong to a pod, - a pod can have multiple containers. .debug[[containers/Init_Systems.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Init_Systems.md)] --- ## What about these zombie processes? - Our application runs as PID 1 in the container. - Our application may or may not be designed to reap zombie processes. - If our application uses subprocesses and doesn't reap them ... ... this can lead to PID exhaustion! (Or, more realistically, to a confusing herd of zombie processes.) - How can we solve this? .debug[[containers/Init_Systems.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Init_Systems.md)] --- ## Tini to the rescue - Docker can automatically provide a minimal `init` process. - This is enabled with `docker run --init ...` - It uses a small init system ([tini](https://github.com/krallin/tini)) as PID 1: - it reaps zombies, - it forwards signals, - it exits when the child exits. - It is totally transparent to our application. - We should use it if our application creates subprocess but doesn't reap them. .debug[[containers/Init_Systems.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Init_Systems.md)] --- class: extra-details ## What about Kubernetes? - Kubernetes does not expose that `--init` option. - However, we can achieve the same result with [Process Namespace Sharing](https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/). - When Process Namespace Sharing is enabled, PID 1 will be `pause`. - That `pause` process takes care of reaping zombies. - Process Namespace Sharing is available since Kubernetes 1.16. - If you're using an older version of Kubernetes ... ... you might have to add `tini` explicitly to your Docker image. .debug[[containers/Init_Systems.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Init_Systems.md)] --- class: pic .interstitial[![Image separating from the next module](https://gallant-turing-d0d520.netlify.com/containers/aerial-view-of-containers.jpg)] --- name: toc-advanced-dockerfile-syntax class: title Advanced Dockerfile Syntax .nav[ [Previous section](#toc-init-systems-and-pid-) | [Back to table of contents](#toc-module-7) | [Next section](#toc-application-configuration) ] .debug[(automatically generated title slide)] --- class: title # Advanced Dockerfile Syntax ![construction](images/title-advanced-dockerfiles.jpg) .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## Objectives We have seen simple Dockerfiles to illustrate how Docker build container images. In this section, we will give a recap of the Dockerfile syntax, and introduce advanced Dockerfile commands that we might come across sometimes; or that we might want to use in some specific scenarios. .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## `Dockerfile` usage summary * `Dockerfile` instructions are executed in order. * Each instruction creates a new layer in the image. * Docker maintains a cache with the layers of previous builds. * When there are no changes in the instructions and files making a layer, the builder re-uses the cached layer, without executing the instruction for that layer. * The `FROM` instruction MUST be the first non-comment instruction. * Lines starting with `#` are treated as comments. * Some instructions (like `CMD` or `ENTRYPOINT`) update a piece of metadata. (As a result, each call to these instructions makes the previous one useless.) .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## The `RUN` instruction The `RUN` instruction can be specified in two ways. With shell wrapping, which runs the specified command inside a shell, with `/bin/sh -c`: ```dockerfile RUN apt-get update ``` Or using the `exec` method, which avoids shell string expansion, and allows execution in images that don't have `/bin/sh`: ```dockerfile RUN [ "apt-get", "update" ] ``` .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## More about the `RUN` instruction `RUN` will do the following: * Execute a command. * Record changes made to the filesystem. * Work great to install libraries, packages, and various files. `RUN` will NOT do the following: * Record state of *processes*. * Automatically start daemons. If you want to start something automatically when the container runs, you should use `CMD` and/or `ENTRYPOINT`. .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## Collapsing layers It is possible to execute multiple commands in a single step: ```dockerfile RUN apt-get update && apt-get install -y wget && apt-get clean ``` It is also possible to break a command onto multiple lines: ```dockerfile RUN apt-get update \ && apt-get install -y wget \ && apt-get clean ``` .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## The `EXPOSE` instruction The `EXPOSE` instruction tells Docker what ports are to be published in this image. ```dockerfile EXPOSE 8080 EXPOSE 80 443 EXPOSE 53/tcp 53/udp ``` * All ports are private by default. * Declaring a port with `EXPOSE` is not enough to make it public. * The `Dockerfile` doesn't control on which port a service gets exposed. .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## Exposing ports * When you `docker run -p ...`, that port becomes public. (Even if it was not declared with `EXPOSE`.) * When you `docker run -P ...` (without port number), all ports declared with `EXPOSE` become public. A *public port* is reachable from other containers and from outside the host. A *private port* is not reachable from outside. .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## The `COPY` instruction The `COPY` instruction adds files and content from your host into the image. ```dockerfile COPY . /src ``` This will add the contents of the *build context* (the directory passed as an argument to `docker build`) to the directory `/src` in the container. .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## Build context isolation Note: you can only reference files and directories *inside* the build context. Absolute paths are taken as being anchored to the build context, so the two following lines are equivalent: ```dockerfile COPY . /src COPY / /src ``` Attempts to use `..` to get out of the build context will be detected and blocked with Docker, and the build will fail. Otherwise, a `Dockerfile` could succeed on host A, but fail on host B. .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## `ADD` `ADD` works almost like `COPY`, but has a few extra features. `ADD` can get remote files: ```dockerfile ADD http://www.example.com/webapp.jar /opt/ ``` This would download the `webapp.jar` file and place it in the `/opt` directory. `ADD` will automatically unpack zip files and tar archives: ```dockerfile ADD ./assets.zip /var/www/htdocs/assets/ ``` This would unpack `assets.zip` into `/var/www/htdocs/assets`. *However,* `ADD` will not automatically unpack remote archives. .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## `ADD`, `COPY`, and the build cache * Before creating a new layer, Docker checks its build cache. * For most Dockerfile instructions, Docker only looks at the `Dockerfile` content to do the cache lookup. * For `ADD` and `COPY` instructions, Docker also checks if the files to be added to the container have been changed. * `ADD` always needs to download the remote file before it can check if it has been changed. (It cannot use, e.g., ETags or If-Modified-Since headers.) .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## `VOLUME` The `VOLUME` instruction tells Docker that a specific directory should be a *volume*. ```dockerfile VOLUME /var/lib/mysql ``` Filesystem access in volumes bypasses the copy-on-write layer, offering native performance to I/O done in those directories. Volumes can be attached to multiple containers, allowing to "port" data over from a container to another, e.g. to upgrade a database to a newer version. It is possible to start a container in "read-only" mode. The container filesystem will be made read-only, but volumes can still have read/write access if necessary. .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## The `WORKDIR` instruction The `WORKDIR` instruction sets the working directory for subsequent instructions. It also affects `CMD` and `ENTRYPOINT`, since it sets the working directory used when starting the container. ```dockerfile WORKDIR /src ``` You can specify `WORKDIR` again to change the working directory for further operations. .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## The `ENV` instruction The `ENV` instruction specifies environment variables that should be set in any container launched from the image. ```dockerfile ENV WEBAPP_PORT 8080 ``` This will result in an environment variable being created in any containers created from this image of ```bash WEBAPP_PORT=8080 ``` You can also specify environment variables when you use `docker run`. ```bash $ docker run -e WEBAPP_PORT=8000 -e WEBAPP_HOST=www.example.com ... ``` .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## The `USER` instruction The `USER` instruction sets the user name or UID to use when running the image. It can be used multiple times to change back to root or to another user. .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## The `CMD` instruction The `CMD` instruction is a default command run when a container is launched from the image. ```dockerfile CMD [ "nginx", "-g", "daemon off;" ] ``` Means we don't need to specify `nginx -g "daemon off;"` when running the container. Instead of: ```bash $ docker run /web_image nginx -g "daemon off;" ``` We can just do: ```bash $ docker run /web_image ``` .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## More about the `CMD` instruction Just like `RUN`, the `CMD` instruction comes in two forms. The first executes in a shell: ```dockerfile CMD nginx -g "daemon off;" ``` The second executes directly, without shell processing: ```dockerfile CMD [ "nginx", "-g", "daemon off;" ] ``` .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- class: extra-details ## Overriding the `CMD` instruction The `CMD` can be overridden when you run a container. ```bash $ docker run -it /web_image bash ``` Will run `bash` instead of `nginx -g "daemon off;"`. .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## The `ENTRYPOINT` instruction The `ENTRYPOINT` instruction is like the `CMD` instruction, but arguments given on the command line are *appended* to the entry point. Note: you have to use the "exec" syntax (`[ "..." ]`). ```dockerfile ENTRYPOINT [ "/bin/ls" ] ``` If we were to run: ```bash $ docker run training/ls -l ``` Instead of trying to run `-l`, the container will run `/bin/ls -l`. .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- class: extra-details ## Overriding the `ENTRYPOINT` instruction The entry point can be overridden as well. ```bash $ docker run -it training/ls bin dev home lib64 mnt proc run srv tmp var boot etc lib media opt root sbin sys usr $ docker run -it --entrypoint bash training/ls root@d902fb7b1fc7:/# ``` .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## How `CMD` and `ENTRYPOINT` interact The `CMD` and `ENTRYPOINT` instructions work best when used together. ```dockerfile ENTRYPOINT [ "nginx" ] CMD [ "-g", "daemon off;" ] ``` The `ENTRYPOINT` specifies the command to be run and the `CMD` specifies its options. On the command line we can then potentially override the options when needed. ```bash $ docker run -d /web_image -t ``` This will override the options `CMD` provided with new flags. .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- ## Advanced Dockerfile instructions * `ONBUILD` lets you stash instructions that will be executed when this image is used as a base for another one. * `LABEL` adds arbitrary metadata to the image. * `ARG` defines build-time variables (optional or mandatory). * `STOPSIGNAL` sets the signal for `docker stop` (`TERM` by default). * `HEALTHCHECK` defines a command assessing the status of the container. * `SHELL` sets the default program to use for string-syntax RUN, CMD, etc. .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- class: extra-details ## The `ONBUILD` instruction The `ONBUILD` instruction is a trigger. It sets instructions that will be executed when another image is built from the image being build. This is useful for building images which will be used as a base to build other images. ```dockerfile ONBUILD COPY . /src ``` * You can't chain `ONBUILD` instructions with `ONBUILD`. * `ONBUILD` can't be used to trigger `FROM` instructions. ??? :EN:- Advanced Dockerfile syntax :FR:- Dockerfile niveau expert .debug[[containers/Advanced_Dockerfiles.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Advanced_Dockerfiles.md)] --- class: pic .interstitial[![Image separating from the next module](https://gallant-turing-d0d520.netlify.com/containers/blue-containers.jpg)] --- name: toc-application-configuration class: title Application Configuration .nav[ [Previous section](#toc-advanced-dockerfile-syntax) | [Back to table of contents](#toc-module-8) | [Next section](#toc-logging) ] .debug[(automatically generated title slide)] --- # Application Configuration There are many ways to provide configuration to containerized applications. There is no "best way" — it depends on factors like: * configuration size, * mandatory and optional parameters, * scope of configuration (per container, per app, per customer, per site, etc), * frequency of changes in the configuration. .debug[[containers/Application_Configuration.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Application_Configuration.md)] --- ## Command-line parameters ```bash docker run jpetazzo/hamba 80 www1:80 www2:80 ``` * Configuration is provided through command-line parameters. * In the above example, the `ENTRYPOINT` is a script that will: - parse the parameters, - generate a configuration file, - start the actual service. .debug[[containers/Application_Configuration.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Application_Configuration.md)] --- ## Command-line parameters pros and cons * Appropriate for mandatory parameters (without which the service cannot start). * Convenient for "toolbelt" services instantiated many times. (Because there is no extra step: just run it!) * Not great for dynamic configurations or bigger configurations. (These things are still possible, but more cumbersome.) .debug[[containers/Application_Configuration.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Application_Configuration.md)] --- ## Environment variables ```bash docker run -e ELASTICSEARCH_URL=http://es42:9201/ kibana ``` * Configuration is provided through environment variables. * The environment variable can be used straight by the program, or by a script generating a configuration file. .debug[[containers/Application_Configuration.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Application_Configuration.md)] --- ## Environment variables pros and cons * Appropriate for optional parameters (since the image can provide default values). * Also convenient for services instantiated many times. (It's as easy as command-line parameters.) * Great for services with lots of parameters, but you only want to specify a few. (And use default values for everything else.) * Ability to introspect possible parameters and their default values. * Not great for dynamic configurations. .debug[[containers/Application_Configuration.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Application_Configuration.md)] --- ## Baked-in configuration ``` FROM prometheus COPY prometheus.conf /etc ``` * The configuration is added to the image. * The image may have a default configuration; the new configuration can: - replace the default configuration, - extend it (if the code can read multiple configuration files). .debug[[containers/Application_Configuration.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Application_Configuration.md)] --- ## Baked-in configuration pros and cons * Allows arbitrary customization and complex configuration files. * Requires writing a configuration file. (Obviously!) * Requires building an image to start the service. * Requires rebuilding the image to reconfigure the service. * Requires rebuilding the image to upgrade the service. * Configured images can be stored in registries. (Which is great, but requires a registry.) .debug[[containers/Application_Configuration.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Application_Configuration.md)] --- ## Configuration volume ```bash docker run -v appconfig:/etc/appconfig myapp ``` * The configuration is stored in a volume. * The volume is attached to the container. * The image may have a default configuration. (But this results in a less "obvious" setup, that needs more documentation.) .debug[[containers/Application_Configuration.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Application_Configuration.md)] --- ## Configuration volume pros and cons * Allows arbitrary customization and complex configuration files. * Requires creating a volume for each different configuration. * Services with identical configurations can use the same volume. * Doesn't require building / rebuilding an image when upgrading / reconfiguring. * Configuration can be generated or edited through another container. .debug[[containers/Application_Configuration.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Application_Configuration.md)] --- ## Dynamic configuration volume * This is a powerful pattern for dynamic, complex configurations. * The configuration is stored in a volume. * The configuration is generated / updated by a special container. * The application container detects when the configuration is changed. (And automatically reloads the configuration when necessary.) * The configuration can be shared between multiple services if needed. .debug[[containers/Application_Configuration.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Application_Configuration.md)] --- ## Dynamic configuration volume example In a first terminal, start a load balancer with an initial configuration: ```bash $ docker run --name loadbalancer jpetazzo/hamba \ 80 goo.gl:80 ``` In another terminal, reconfigure that load balancer: ```bash $ docker run --rm --volumes-from loadbalancer jpetazzo/hamba reconfigure \ 80 google.com:80 ``` The configuration could also be updated through e.g. a REST API. (The REST API being itself served from another container.) .debug[[containers/Application_Configuration.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Application_Configuration.md)] --- ## Keeping secrets .warning[Ideally, you should not put secrets (passwords, tokens...) in:] * command-line or environment variables (anyone with Docker API access can get them), * images, especially stored in a registry. Secrets management is better handled with an orchestrator (like Swarm or Kubernetes). Orchestrators will allow to pass secrets in a "one-way" manner. Managing secrets securely without an orchestrator can be contrived. E.g.: - read the secret on stdin when the service starts, - pass the secret using an API endpoint. .debug[[containers/Application_Configuration.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Application_Configuration.md)] --- class: pic .interstitial[![Image separating from the next module](https://gallant-turing-d0d520.netlify.com/containers/chinook-helicopter-container.jpg)] --- name: toc-logging class: title Logging .nav[ [Previous section](#toc-application-configuration) | [Back to table of contents](#toc-module-8) | [Next section](#toc-orchestration-an-overview) ] .debug[(automatically generated title slide)] --- # Logging In this chapter, we will explain the different ways to send logs from containers. We will then show one particular method in action, using ELK and Docker's logging drivers. .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## There are many ways to send logs - The simplest method is to write on the standard output and error. - Applications can write their logs to local files. (The files are usually periodically rotated and compressed.) - It is also very common (on UNIX systems) to use syslog. (The logs are collected by syslogd or an equivalent like journald.) - In large applications with many components, it is common to use a logging service. (The code uses a library to send messages to the logging service.) *All these methods are available with containers.* .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## Writing on stdout/stderr - The standard output and error of containers is managed by the container engine. - This means that each line written by the container is received by the engine. - The engine can then do "whatever" with these log lines. - With Docker, the default configuration is to write the logs to local files. - The files can then be queried with e.g. `docker logs` (and the equivalent API request). - This can be customized, as we will see later. .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## Writing to local files - If we write to files, it is possible to access them but cumbersome. (We have to use `docker exec` or `docker cp`.) - Furthermore, if the container is stopped, we cannot use `docker exec`. - If the container is deleted, the logs disappear. - What should we do for programs who can only log to local files? -- - There are multiple solutions. .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## Using a volume or bind mount - Instead of writing logs to a normal directory, we can place them on a volume. - The volume can be accessed by other containers. - We can run a program like `filebeat` in another container accessing the same volume. (`filebeat` reads local log files continuously, like `tail -f`, and sends them to a centralized system like ElasticSearch.) - We can also use a bind mount, e.g. `-v /var/log/containers/www:/var/log/tomcat`. - The container will write log files to a directory mapped to a host directory. - The log files will appear on the host and be consumable directly from the host. .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## Using logging services - We can use logging frameworks (like log4j or the Python `logging` package). - These frameworks require some code and/or configuration in our application code. - These mechanisms can be used identically inside or outside of containers. - Sometimes, we can leverage containerized networking to simplify their setup. - For instance, our code can send log messages to a server named `log`. - The name `log` will resolve to different addresses in development, production, etc. .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## Using syslog - What if our code (or the program we are running in containers) uses syslog? - One possibility is to run a syslog daemon in the container. - Then that daemon can be setup to write to local files or forward to the network. - Under the hood, syslog clients connect to a local UNIX socket, `/dev/log`. - We can expose a syslog socket to the container (by using a volume or bind-mount). - Then just create a symlink from `/dev/log` to the syslog socket. - Voilà! .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## Using logging drivers - If we log to stdout and stderr, the container engine receives the log messages. - The Docker Engine has a modular logging system with many plugins, including: - json-file (the default one) - syslog - journald - gelf - fluentd - splunk - etc. - Each plugin can process and forward the logs to another process or system. .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## A word of warning about `json-file` - By default, log file size is unlimited. - This means that a very verbose container *will* use up all your disk space. (Or a less verbose container, but running for a very long time.) - Log rotation can be enabled by setting a `max-size` option. - Older log files can be removed by setting a `max-file` option. - Just like other logging options, these can be set per container, or globally. Example: ```bash $ docker run --log-opt max-size=10m --log-opt max-file=3 elasticsearch ``` .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## Demo: sending logs to ELK - We are going to deploy an ELK stack. - It will accept logs over a GELF socket. - We will run a few containers with the `gelf` logging driver. - We will then see our logs in Kibana, the web interface provided by ELK. *Important foreword: this is not an "official" or "recommended" setup; it is just an example. We used ELK in this demo because it's a popular setup and we keep being asked about it; but you will have equal success with Fluent or other logging stacks!* .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## What's in an ELK stack? - ELK is three components: - ElasticSearch (to store and index log entries) - Logstash (to receive log entries from various sources, process them, and forward them to various destinations) - Kibana (to view/search log entries with a nice UI) - The only component that we will configure is Logstash - We will accept log entries using the GELF protocol - Log entries will be stored in ElasticSearch, and displayed on Logstash's stdout for debugging .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## Running ELK - We are going to use a Compose file describing the ELK stack. - The Compose file is in the container.training repository on GitHub. ```bash $ git clone https://github.com/jpetazzo/container.training $ cd container.training $ cd elk $ docker-compose up ``` - Let's have a look at the Compose file while it's deploying. .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## Our basic ELK deployment - We are using images from the Docker Hub: `elasticsearch`, `logstash`, `kibana`. - We don't need to change the configuration of ElasticSearch. - We need to tell Kibana the address of ElasticSearch: - it is set with the `ELASTICSEARCH_URL` environment variable, - by default it is `localhost:9200`, we change it to `elasticsearch:9200`. - We need to configure Logstash: - we pass the entire configuration file through command-line arguments, - this is a hack so that we don't have to create an image just for the config. .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## Sending logs to ELK - The ELK stack accepts log messages through a GELF socket. - The GELF socket listens on UDP port 12201. - To send a message, we need to change the logging driver used by Docker. - This can be done globally (by reconfiguring the Engine) or on a per-container basis. - Let's override the logging driver for a single container: ```bash $ docker run --log-driver=gelf --log-opt=gelf-address=udp://localhost:12201 \ alpine echo hello world ``` .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## Viewing the logs in ELK - Connect to the Kibana interface. - It is exposed on port 5601. - Browse http://X.X.X.X:5601. .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## "Configuring" Kibana - Kibana should offer you to "Configure an index pattern": in the "Time-field name" drop down, select "@timestamp", and hit the "Create" button. - Then: - click "Discover" (in the top-left corner), - click "Last 15 minutes" (in the top-right corner), - click "Last 1 hour" (in the list in the middle), - click "Auto-refresh" (top-right corner), - click "5 seconds" (top-left of the list). - You should see a series of green bars (with one new green bar every minute). - Our 'hello world' message should be visible there. .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- ## Important afterword **This is not a "production-grade" setup.** It is just an educational example. Since we have only one node , we did set up a single ElasticSearch instance and a single Logstash instance. In a production setup, you need an ElasticSearch cluster (both for capacity and availability reasons). You also need multiple Logstash instances. And if you want to withstand bursts of logs, you need some kind of message queue: Redis if you're cheap, Kafka if you want to make sure that you don't drop messages on the floor. Good luck. If you want to learn more about the GELF driver, have a look at [this blog post]( https://jpetazzo.github.io/2017/01/20/docker-logging-gelf/). .debug[[containers/Logging.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Logging.md)] --- class: pic .interstitial[![Image separating from the next module](https://gallant-turing-d0d520.netlify.com/containers/container-cranes.jpg)] --- name: toc-orchestration-an-overview class: title Orchestration, an overview .nav[ [Previous section](#toc-logging) | [Back to table of contents](#toc-module-8) | [Next section](#toc-links-and-resources) ] .debug[(automatically generated title slide)] --- # Orchestration, an overview In this chapter, we will: * Explain what is orchestration and why we would need it. * Present (from a high-level perspective) some orchestrators. .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- class: pic ## What's orchestration? ![Joana Carneiro (orchestra conductor)](images/conductor.jpg) .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## What's orchestration? According to Wikipedia: *Orchestration describes the __automated__ arrangement, coordination, and management of complex computer systems, middleware, and services.* -- *[...] orchestration is often discussed in the context of __service-oriented architecture__, __virtualization__, provisioning, Converged Infrastructure and __dynamic datacenter__ topics.* -- What does that really mean? .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Example 1: dynamic cloud instances -- - Q: do we always use 100% of our servers? -- - A: obviously not! .center[![Daily variations of traffic](images/traffic-graph.png)] .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Example 1: dynamic cloud instances - Every night, scale down (by shutting down extraneous replicated instances) - Every morning, scale up (by deploying new copies) - "Pay for what you use" (i.e. save big $$$ here) .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Example 1: dynamic cloud instances How do we implement this? - Crontab - Autoscaling (save even bigger $$$) That's *relatively* easy. Now, how are things for our IAAS provider? .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Example 2: dynamic datacenter - Q: what's the #1 cost in a datacenter? -- - A: electricity! -- - Q: what uses electricity? -- - A: servers, obviously - A: ... and associated cooling -- - Q: do we always use 100% of our servers? -- - A: obviously not! .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Example 2: dynamic datacenter - If only we could turn off unused servers during the night... - Problem: we can only turn off a server if it's totally empty! (i.e. all VMs on it are stopped/moved) - Solution: *migrate* VMs and shutdown empty servers (e.g. combine two hypervisors with 40% load into 80%+0%, and shut down the one at 0%) .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Example 2: dynamic datacenter How do we implement this?  *Scheduling* becomes more complex. .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## What is scheduling? According to Wikipedia (again): *In computing, scheduling is the method by which threads, processes or data flows are given access to system resources.* The scheduler is concerned mainly with: - throughput (total amount of work done per time unit); - turnaround time (between submission and completion); - response time (between submission and start); - waiting time (between job readiness and execution); - fairness (appropriate times according to priorities). In practice, these goals often conflict. **"Scheduling" = decide which resources to use.** .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Exercise 1 - You have: - 5 hypervisors (physical machines) - Each server has: - 16 GB RAM, 8 cores, 1 TB disk - Each week, your team requests: - one VM with X RAM, Y CPU, Z disk Scheduling = deciding which hypervisor to use for each VM. Difficulty: easy! .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Exercise 2 - You have: - 1000+ hypervisors (and counting!) - Each server has different resources: - 8-500 GB of RAM, 4-64 cores, 1-100 TB disk - Multiple times a day, a different team asks for: - up to 50 VMs with different characteristics Scheduling = deciding which hypervisor to use for each VM. Difficulty: ??? .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Exercise 2 - You have: - 1000+ hypervisors (and counting!) - Each server has different resources: - 8-500 GB of RAM, 4-64 cores, 1-100 TB disk - Multiple times a day, a different team asks for: - up to 50 VMs with different characteristics Scheduling = deciding which hypervisor to use for each VM. ![Troll face](images/trollface.png) .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Exercise 3 - You have machines (physical and/or virtual) - You have containers - You are trying to put the containers on the machines - Sounds familiar? .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- class: pic ## Scheduling with one resource .center[![Not-so-good bin packing](images/binpacking-1d-1.gif)] ## We can't fit a job of size 6 :( .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- class: pic ## Scheduling with one resource .center[![Better bin packing](images/binpacking-1d-2.gif)] ## ... Now we can! .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- class: pic ## Scheduling with two resources .center[![2D bin packing](images/binpacking-2d.gif)] .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- class: pic ## Scheduling with three resources .center[![3D bin packing](images/binpacking-3d.gif)] .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- class: pic ## You need to be good at this .center[![Tangram](images/tangram.gif)] .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- class: pic ## But also, you must be quick! .center[![Tetris](images/tetris-1.png)] .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- class: pic ## And be web scale! .center[![Big tetris](images/tetris-2.gif)] .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- class: pic ## And think outside (?) of the box! .center[![3D tetris](images/tetris-3.png)] .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- class: pic ## Good luck! .center[![FUUUUUU face](images/fu-face.jpg)] .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## TL,DR * Scheduling with multiple resources (dimensions) is hard. * Don't expect to solve the problem with a Tiny Shell Script. * There are literally tons of research papers written on this. .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## But our orchestrator also needs to manage ... * Network connectivity (or filtering) between containers. * Load balancing (external and internal). * Failure recovery (if a node or a whole datacenter fails). * Rolling out new versions of our applications. (Canary deployments, blue/green deployments...) .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Some orchestrators We are going to present briefly a few orchestrators. There is no "absolute best" orchestrator. It depends on: - your applications, - your requirements, - your pre-existing skills... .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Nomad - Open Source project by Hashicorp. - Arbitrary scheduler (not just for containers). - Great if you want to schedule mixed workloads. (VMs, containers, processes...) - Less integration with the rest of the container ecosystem. .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Mesos - Open Source project in the Apache Foundation. - Arbitrary scheduler (not just for containers). - Two-level scheduler. - Top-level scheduler acts as a resource broker. - Second-level schedulers (aka "frameworks") obtain resources from top-level. - Frameworks implement various strategies. (Marathon = long running processes; Chronos = run at intervals; ...) - Commercial offering through DC/OS by Mesosphere. .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Rancher - Rancher 1 offered a simple interface for Docker hosts. - Rancher 2 is a complete management platform for Docker and Kubernetes. - Technically not an orchestrator, but it's a popular option. .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Swarm - Tightly integrated with the Docker Engine. - Extremely simple to deploy and setup, even in multi-manager (HA) mode. - Secure by default. - Strongly opinionated: - smaller set of features, - easier to operate. .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- ## Kubernetes - Open Source project initiated by Google. - Contributions from many other actors. - *De facto* standard for container orchestration. - Many deployment options; some of them very complex. - Reputation: steep learning curve. - Reality: - true, if we try to understand *everything*; - false, if we focus on what matters. .debug[[containers/Orchestration_Overview.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/Orchestration_Overview.md)] --- class: title, self-paced Thank you! .debug[[shared/thankyou.md](https://github.com/jpetazzo/container.training/tree/master/slides/shared/thankyou.md)] --- class: title, in-person That's all, folks! Questions? ![end](images/end.jpg) .debug[[shared/thankyou.md](https://github.com/jpetazzo/container.training/tree/master/slides/shared/thankyou.md)] --- class: pic .interstitial[![Image separating from the next module](https://gallant-turing-d0d520.netlify.com/containers/container-housing.jpg)] --- name: toc-links-and-resources class: title Links and resources .nav[ [Previous section](#toc-orchestration-an-overview) | [Back to table of contents](#toc-module-9) | [Next section](#toc-) ] .debug[(automatically generated title slide)] --- # Links and resources - [Docker Community Slack](https://community.docker.com/registrations/groups/4316) - [Docker Community Forums](https://forums.docker.com/) - [Docker Hub](https://hub.docker.com) - [Docker Blog](https://blog.docker.com/) - [Docker documentation](https://docs.docker.com/) - [Docker on StackOverflow](https://stackoverflow.com/questions/tagged/docker) - [Docker on Twitter](https://twitter.com/docker) - [Play With Docker Hands-On Labs](https://training.play-with-docker.com/) .footnote[These slides (and future updates) are on → https://container.training/] .debug[[containers/links.md](https://github.com/jpetazzo/container.training/tree/master/slides/containers/links.md)]


--- layout: default permalink: /stories/ --- {{ site.name }}'s blog {% for nav in site.nav %} {{ nav.name }} {% endfor %}


HTML-CSS-Splitter
Split inline CSS from HTML
menu
About
html-css-splitter
Converters
Converter Colors
Converter Screen Sizes
Tools
Reading Times
visibility
code
Input a inline-style html-code.
HTML with inline CSS - Input Area
Convert
cached
Clean up
delete
Fill in demo content
accessible
HTML Output Area
CSS Output Area


5 references to SetHasInstanceMember
System.Management.Automation (5)
engine\MshMemberInfo.cs (1)
4677
PSGetMemberBinder.
SetHasInstanceMember
(memberToBeAdded.Name);
engine\serialization.cs (4)
3646
PSGetMemberBinder.
SetHasInstanceMember
("ToString");
3829
PSGetMemberBinder.
SetHasInstanceMember
("GetType");
3872
PSGetMemberBinder.
SetHasInstanceMember
(name);
3879
PSGetMemberBinder.
SetHasInstanceMember
(note.Name);


BLACKJACK
Your goal is to draw cards that total 21, or
come closer to 21 than the dealer without going over.
DEAL
HIT
STAND
Game Status:
Player:
Dealer


Study Year is required!
Gender is required!
Name is required!
Parent Name is required!
Email ID is required!
Please select valid date
Phone Number is required!
Location is required!
{{loc.description}}


APPETIZERS
ENTRÉES
DESSERTS
COCKTAILS
Hearty, organic entrées
that taste just a little
better than homemade.
Beef Pot Pie
Herb Roasted
Chicken
Creamy
Pesto Pasta
Spinach Filo Puffs
Mediterranean Seafood Stew


Bootstrap Basics
Click Me!
Click Me!
Click Me!
Bootstrap Docs
This is a jumbotron
afd;kasdfaskdfasdfa;flwker;kaf af;djksf ea;kfeafa
Hi there


Go from zero to job ready in an immersive environment. Learn the latest skills from industry professionals and connect with employers through our Career Support program.
Our free, one-night crash courses are the perfect way to get your hands dirty with a fun project and gain the confidence to explore the world of programming.
If you already lead or work on a development team, our recruiting, onboarding, reskilling and upskilling programs provide proven pathways to grow your team.
We offer learning programs that cover the core components of web development. Each course is designed to help people reach their learning and career goals.
- Jesse Crow, UI Developer at IBM Design
Julie worked for an oil company in Texas and struggled to balance 100-hour-work weeks and family life. Today she is happily employed at an Internet marketing company.
Julie worked for an oil company in Texas and struggled to balance 100-hour-work weeks and family life. Today she is happily employed at an Internet marketing company.
We work with amazing companies around the world, connecting them with our graduates, training their developers and partnering with them to impact the communities around each of our campuses.
Whether you're thinking about starting a new career, interested in learning more about our programs for individuals or businesses, or just want to chat, we're here and we'd love to connect with you. Shoot us an email or your phone number and we'll get back to you soon, or you can chat with us right away using the chat icon below.


--- layout: page title: Projects subtitle: Things I have made. description: Matthew's projects page. permalink: /projects full_logo: true sitemap: priority: 0.9 tags: - name: matthew albert ---


{% extends "admin/base_site.html" %} {% load i18n l10n %} {% load url from future %} {% load admin_urls %} {% block breadcrumbs %}
Click the Choose File button to select the CSV file that contains user information.


For those that think they might want to do way advanced prep:
#!/bin/bash
SHELL=/bin/sh PATH=/bin:/sbin:/usr/bin:/usr/sbin
value=`cat $HOME/komodo/src/assetchains.json`
for name in $(jq -r '.[].ac_name' <<<${value});
do
String="https://bootstrap.dexstats.info/$name-bootstrap.tar.gz"
eval "$String"
done
because who's got time to do it manually?


We use web scraping to get the weather forecast and use our models to predict the rate of crime of the temperatures
The temperatures we got from weather.com are as the following.
| Now | 1 days | 2 days | 3 days | 4 days | 5 days | 6 days | 7 days | 8 days | 9 days |
|---|---|---|---|---|---|---|---|---|---|
We use our model to predict the rate of crime using the temperatures above. The result as the following table.
| Now | 1 days | 2 days | 3 days | 4 days | 5 days | 6 days | 7 days | 8 days | 9 days |
|---|---|---|---|---|---|---|---|---|---|


"It has become appallingly obvious that our technology has exceeded our humanity." -Albert Einstein
"The world won't care about your self-esteem. The world will expect you to accomplish something BEFORE you feel good about yourself." -Bill Gates
"Fork your repositories wisely" -Someone in Moringa


Oklahoma Supreme Court blocks abortion law
OKLAHOMA CITY (AP) — The Oklahoma Supreme Court has temporarily blocked a ban on a second-trimester abortion procedure.
The high court acted Monday after an Oklahoma abortion clinic appealed a lower court judge’s decision to uphold a ban on a second-trimester abortion procedure.
The Tulsa Women’s Reproductive Clinic requested the temporary injunction to put the law on hold, telling justices that the law would be detrimental for women.
The 2015 law would restrict the use of instruments in dilation and evacuation abortions after 14 weeks of pregnancy, except when needed to save the woman’s life or prevent a serious risk to her health. It had been on hold while the legal challenge was pending.
State numbers show nearly 7% of about 5,000 abortions performed in Oklahoma in 2018 were performed using this method.
Sign up for Daily Newsletters
Copyright © 2019 The Washington Times, LLC.
Please read our comment policy before commenting.


--- layout: page title: About permalink: /about/ ---
Since our certification as a colony in January 2013, the ΤΔ Chapter of ΘΤ has grown to over 50 members. We have dedicated ourselves to the service of the College of Engineering and Computing as well as the greater Oxford community. We have participated in service events with national charities such as Habitat for Humanity, Feed My Starving Children, and Relay for Life, as well as local organizations such as an animal shelter and the Cincinnati Reds. We have worked with the College of Engineering and Computing on events such as Engineers' Week, Make It Miami tours, and led tutoring sessions for engineering students in any classes.


+91 7219 - 446 - 042
<EMAIL_ADDRESS>Select Plan
Today's Menu
ASBS Tiffin Servics
Home
(current)
About
Contact
Admin
Login
Registration


Welcome to Your Nodal Application!
View the controller that renders this template ./app/controllers/index_controller.js to change the data sent to it, or modify the template directly.
We really recommend you start playing with creating RESTful API endpoints using the built in commands:
- nodal db:create
- nodal db:prepare
- nodal g:model --user
- nodal g:controller v1 --for:user
- nodal db:migrate
To get a good feeling about how Nodal works and its intended use.
You can see all of the built in nodal commands by running nodal help
Important Note:
For database usage, please make sure you have Postgres installed. You must also uncomment this.useDatabase and the
database import in ./app/app.js. Please also check to make sure your database credentials are correct in ./config/db.json
- Happy Developing!


Skip to main content
Skip to main navigation
Australia.gov.au
Menu
Close
Home
Information and Services
About Government
News and Social Media
About Australia
myGov
Home
News and Social Media
Australian Government media releases
Subscribe
Choose the Australian Government portfolios of interest to you
We will only send you media releases from agencies of the portfolios you choose.
Select all
Agriculture
Attorney-General's
Communication and the Arts
Defence
Education
Employment, Skills, Small and Family Business
Enviorment and Energy
Finance
Foreign Affairs and Trade
Health
Home Affairs
Industry, Innovation and Science
Infrastructure, Transport, Cities and Regional Development
Parliamentary Departments
Prime Minister and Cabinet
Services Australia
Social Services
Treasury
Veteran's Affairs
Next


Mourinho to raid Lille again for Osimhen
by Ebenezer BajelaEbenezer Bajela
Super Eagles and Lille forward, Victor Osimhen, is believed to be the primary target for newly appointed Tottenham coach Jose Mourinho who could be on his way back to the French outfit to poach the club of its most priced asset.
Mourinho raided Joao Sacramento and Nuno Santos from LOSC after being announced as Spurs new manager but the Portuguese’s raiding of the Lille based club may not be over as he is set to make a return in January.
And according to reports in France, the Portuguese has shortlisted two of his January targets with just over a month to go before the transfer window reopens at the start of the new year.
Lille duo Victor Osimhen and Boubakary Soumare are the players said to be on Mourinho’s radar as reported by Jeunesfooteux.
“Rumor has it that Tottenham’s new coach, José Mourinho, has caught on to two LOSC players – striker Victor Osimhen and midfielder Boubakary Soumaré,” the website reads.
Both players are only 20-years-old, though they play in different positions; France youth international Soumare is a midfielder, while Osimhen is a striker who has already been capped eight times by Nigeria’s senior team and scoring four goals in the process.
Copyright PUNCH.
All rights reserved. This material, and other digital content on this website, may not be reproduced, published, broadcast, rewritten or redistributed in whole or in part without prior express written permission from PUNCH.
Contact: theeditor@punchng.com


🔊 PODCAST | Is BigTril's Parte After Parte a Hip Hop song?
by AdminIn this podcast, Richard Wetaya discusses whether BigTril's hit song Parte After Parte should be categorized as Hip Hop.
In this podcast, Richard Wetaya discusses whether BigTril's hit song Parte After Parte should be categorized as Hip Hop.


Wasted is the upshot of a reflection on anonymous design as well as a unit of rebel objects who suffer from their conditions of being constantly manipulated and then forgotten by men. Every object has been rethought and rebranded to become a new product at all, from the name to the packaging design and their communication & logo design.
SHOP NOW
Supporting our cause you’ll become the owner of a unique piece of design, reestablishing a more correct and enjoyable relashionship with everyday art. You can also be delighted by awesome packages made with better materials and designed by the best Italian minds
FuoriSalone
Milano
17 − 22.04.2018
Via Sbodio 9,
Lambrate District
Milan, Italy
@DIN - Design In
During the Milan Design Week
This is not an e-commerce, but we’re pleased to invite you to see and touch by yourself the object you’ve chosen and many other more during our great exhibit
in Milan.
WASTED is an autonomous and independent movement composed by sick and tired everyday objects.
They are now fighting to escape their soul-destroyingncondition of life. Ungratefulness and wearing use, this is what anonymous design objects are intended for. But now, a new era’s coming, a big revolution: they’re facing a path of rebirth and redesign, trying to find their real identity and expectations.
They want to live their dream.
To do that, WASTED objects reinvent theirselves in name, aesthetics and function.
The other aim of the organization is to sensitize people in their relashionship with design, in particular
which come from everyday uses. To learn respect and to show that this kind of design is the most
important for human’s life.


Thank you for your contribution. Your appliance is under review.
If it all looks good we will publish it straight away, otherwise we will be in touch with you to make the necessary changes.
{% for keyword in keywords %}
{{ keyword.name }}
{% endfor %}
{% endif %}
{% if appliance.project_supported %}
Chameleon Supported
{% endif %}
{% if appliance.project_flagged %}
This project is flagged
{% endif %}
{% if appliance.chi_tacc_appliance_id or appliance.chi_uc_appliance_id or appliance.kvm_tacc_appliance_id %}


2020 dodge challenger celebrates 50th anniversary with special gold edition
by tim spearsthere’s a birthday at the LA auto show 2019 as the dodge challenger celebrates its 50th anniversary. the special and highly limited car marks its golden anniversary with a unique ‘gold rush’ exterior paint color. it adorns the famed muscular design which looks as powerful as ever side-by-side its heritage aesthetic from 1970.
‘2020 marks the 50th anniversary of the dodge challenger, one of the most storied muscle cars in history,‘ said tim kuniskis, global head of alfa romeo and head of passenger cars – dodge, SRT, chrysler and fiat, FCA – north america. ‘that history was built on a few key things: big attitude and big muscle, loud colors and loud exhausts, and an enthusiast community that is as unique and diverse as our special editions. so, to commemorate the 50th, we’re celebrating with a cool new special edition that checks all of these boxes.‘
as well as its golden exterior color, the 2020 dodge challenger 50th anniversary features a body-color shaker hood, which is mounted to the engine to ‘shake’ with the movement of the powertrain. this powerful element contrasts dramatically with its satin black hand-painted hood and black-wrapped roof and decklid. anniversary details include ‘50’ logo badges on the grille and spoiler as well as illuminated air catcher headlamps.
inside, the special edition features its anniversary badge integrated into the instrument panel and startup screens as well as the seat backs, door bolsters and floor mats. the interior adorns black, heated and ventilated nappa leather mixed with alcantara performance seats. these are finished off with sepia accent embroidery, which is then stitched along the center console and around other elements on the dashboard.
there will be only 1960 models of the 2020 dodge challenger 50th anniversary edition.
project info:
brand: dodge
model: challenger 50th anniversary
year: 2020
event: LA auto show 2019


this is
any common utility required while developing go projects comes here to evolve
Reach out to us on developer channel for more details.
Golang utility package bundle
,any common utility required while developing go projects comes here to evolve


We’ll be using a subset of the Chinook test database as an example. Create one file with the following contents (which I’ll call runserver.py):
from sandman.model import register, activate, Model
class Artist(Model):
__tablename__ = 'Artist'
class Album(Model):
__tablename__ = 'Album'
class Playlist(Model):
__tablename__ = 'Playlist'
class Genre(Model):
__tablename__ = 'Genre'
register((Artist, Album, Playlist))
register(Genre)
activate()
from sandman import app, db
app.config['SQLALCHEMY_DATABASE_URI'] = '<your database connection string (using SQLAlchemy)'
app.run()
Then simply run:
python runserver.py
and try curling your new REST API service!
Of course, you don’t actually need to tell sandman about your tables; it’s perfectly capable of introspecting all of them. To use introspection to make all of your database tables available via the admin and REST API, simply remove all model code and call activate() without ever registering a model.
Before we discuss the example code above in more detail, we should discuss some REST API basics first. The most important concept is that of a resource. Resources are sources of information, and the API is an interface to this information. That is, resources are the actual “objects” manipulated by the API. In sandman, each row in a database table is considered a resource. Even though the example above is short, let’s walk through it step by step.
A Model represents a table in your database. You control which tables to expose in the API through the creation of classes which inherit from sandman.model.models.Model. The only attribute you must define in your class is the __tablename__ attribute. sandman uses this to map your class to the corresponding database table. From there, sandman is able to divine all other properties of your tables. Specifically, sandman creates the following:
In the code above, we created 4 sandman.model.models.Model classes that correspond to tables in our database. If we wanted to change the endpoint for a class (the default endpoint is simply the class’s name pluralized), we would do so by setting the __endpoint__ attribute in the definition of the class:
class Genre(Model):
__tablename__ = 'Genre'
__endpoint__ = 'styles'
Now we would point our browser (or curl) to localhost:5000/styles to retrieve the resources in the Genre table.
Many times, we’d like to specify that certain actions can only be carried out against certain types of resources. If we wanted to prevent API users from deleting any Genre resources, for example, we could specify this implicitly by defining the __methods__ attribute and leaving it out, like so:
class Genre(Model):
__tablename__ = 'Genre'
__endpoint__ = 'styles'
__methods__ = ('GET', 'POST', 'PATCH', 'PUT')
For each call into the API, the HTTP method used is validated against the acceptable methods for that resource.
Specifying which HTTP methods are acceptable gives rather coarse control over how a user of the API can interact with our resources. For more granular control, custom validation functions can be specified. To do so, simply define a static method named validate_<METHOD>, where <METHOD> is the HTTP method the validation function should validate. To validate the POST method on Genres, we would define the method validate_POST, like so:
class Genre(Model):
__tablename__ = 'Genre'
__endpoint__ = 'styles'
__methods__ = ('GET', 'POST', 'PATCH', 'PUT')
@staticmethod
def validate_POST(self, resource=None):
if isinstance(resource, list):
return True
# No classical music!
return resource and resource.Name != 'classical'
The validate_POST method is called after the would-be resource is created, trading a bit of performance for a simpler interface. Instead of needing to inspect the incoming HTTP request directly, you can make validation decisions based on the resource itself.
Note that the resource parameter can be either a single resource or a collection of resources, so it’s usually necessary to check which type you’re dealing with. This will likely change in a future version of sandman.
In a “real” project, you should divide the code into at least two files: one with the Model definitions (models.py) and the other with the configuration and the app.run() call (runserver.py).


Hey! I'm Johvonna Murray-Bradshaw: an intelligent, hard working, goal oriented Canadian taking on the world one day at a time.
I study Mechatronics Engineering at the University of Waterloo, which features a co-op program from which I have earned over a year of valuable hands-on experience working at leading companies within my field.
My biggest passions lie in the design and construction of electro-mechanical systems. In other words: I love all things robotics. Apart from robots, I have an affinity for elephants, and a desire to make positive connections with people of all kinds.
Study hard. Travel often. Set goals. Side projects. Hack-a-Thons. Drinking copious amounts of tea. Perpetually learning. Laughing hard. And never, ever hesitating to challenge myself in new ways.
I will be spending my upcoming co-op term working on the hardware prototyping and product development of connected machines at Rapyuta Robotics. Details to follow!
Contributed to the development of Quanser's qdex™ mobile application by designing numerous integration tests in C#, writing a Python script to automate the daily backup of the app’s database, and creating new app features for iOS and Android devices. Additionally performed extensive trade-off analysis between Azure SQL, Neo4j, and MongoDB databases to determine the optimal database for qdex’s functionality.
Designed cold forge tooling responsible for the mass production of steel parts purchased by leading automotive manufacturers. Specialized in using AutoCAD to design die moulds of bushings, collars, and tensioners.
Designed the patent pending CAI Laxity Pro 1000 - a 3D printed device that diagnoses the disability of Chronic Ankle Instability. Created all device components using Solidworks and Materialise software, and actuated the system with a pneumatic circuit.
Ameliorated the Wealth Management Technology Department's web feed creation protocol by designing a Feed Creation Standard. Its implementation into over 500 live file transferring feeds saved Production Application Analyst hours of repetitive work. Awarded CIBC's Outstanding Co-op Student Award.
Worked for 24 hours a day during 2 week sessions, directly with a group of 8 campers ranging between the ages of 5 to 14. Planned incredibly fun wilderness, swimming, and boating activies. Lead flat water canoe trips through Northern Ontario.


| chipKIT Sketch Help |
Next, select Tools>Board>Boards Manager to open the Boards Manager dialog (see Figure: Open the Boards Manager).Figure: Open the Boards ManagerWhen the Boards Manager dialog opens, type "chipKIT" in the search box to find the "chipKIT by chipKIT Community." Click anywhere in the body of the text that displays to reveal the Install button. Click Install (see Figure: Install the Core). After the core is installed, close the Boards Manager window.Figure: Install the Core
|
Microchip Support
Help Updated: 2017-04-13 14:25:13Z
Help Page: 6
|


WikiLeaks<EMAIL_ADDRESS>leaked emails, 2009 mail archive
This message is part of a particular mailbox provided at WikiLeaks
and it should be discussed here.
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Division
- To: Rich Stanton<EMAIL_ADDRESS>- Subject: Division
- From: Michael Connor<EMAIL_ADDRESS>- Date: Fri, 2 Feb 2007 08:59:09 -0800 (PST)
- Domainkey-signature: a=rsa-sha1; q=dns; c=nofws; s=s1024; d=yahoo.com; h=X-YMail-OSG:Received:Date:From:Subject:To:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-ID; b=N7Gc502kLt1TKvmWCF2H+3oqPi2c5G1tkdJt5KuFDMNr1SsinnrUnLsHPZESCq/HCN6z/BL0AT0oC6R3s51RBYDKqN159bk5qElg/nuoLRKdRkRST+7UMwRLVDu6UBtOl/HMjlLBxEjcc3j6lpGokKLCZPgXTQctsPAfyQ0CrC4=;
Brother Rich,
Yahweh's blessing and greetings from his humble servent. I'm writing to you regarding the lastest news about the division being attempted by some of the Leadership of the Aryan Nation/CJCC! As I understand it, our (so called) Leadership wants to abandon the teachings of Pastor Butler and isolate ourselves from others that are like minded towards the race that Yahweh has chosen. I'd like to express my unwillingness to support such a decision. We need to unite not isolate ourselves in this movement. Even if some do not understand the beliefs set forth by Yahweh, they still unknowingly support his cause. Like the saying goes "there's safety in numbers" and making a move like this will only advance the ZOG ideology and help the Jews take over. I have plenty of other concerns regarding this matter, but if our current Leadership wants to pursue this "separation", then
maybe we need different Leadership!
Leadership isn't easy and sometimes you need to do what's best for the order, even if it's against the masses, but I cannot believe that Yahweh is guiding this action.
Maybe the current Leadership needs to step back temporarily, if it's getting too difficult given whatever situation is pressuring this? Or maybe additional Leadership needs to be added to help share the burden? Which ever way is fine with me, but separating/isolating ourselves is not in the best interest to advance Yahweh's beliefs and teachings.
May Yahweh continue to Bless AN/CJCC
His servent 14/88
Brother Michael
Never Miss an Email
Stay connected with Yahoo! Mail on your mobile. Get started!
Entry for this page on WikiLeaks |
Discussion page on WikiLeaks |
wikileaks.org
Date Index |
Thread Index |
Author Index


COVID-19 affects different people in different ways. Most infected people will develop mild to moderate illness and recover without hospitalization.
-
Most Common symtoms
- *fever
- *dry cough
- *tiredness Less Common Symtoms
- *aches and pains
- *sore throat
- *diarrhoea
- *conjunctivitis
- *headache
- *loss of taste or smell
- *a rash on skin, or discolouration of fingers or toes