├── Module-0 ├── Readme.md └── images │ └── console-logon.gif ├── Module-1 ├── Exercise-1 │ ├── Readme.md │ └── images │ │ └── docker-pull.gif ├── Exercise-2 │ ├── Readme.md │ └── images │ │ └── docker-run.gif ├── Readme.md └── images │ └── docker-images.png ├── Module-2 ├── Exercise-1 │ ├── Readme.md │ ├── images │ │ └── docker-build.gif │ └── scripts │ │ └── Dockerfile ├── Exercise-2 │ ├── Readme.md │ └── images │ │ ├── cpx-blog-1-footer.png │ │ ├── cpx-blog-2-footer.png │ │ └── docker-run-new.gif └── Readme.md ├── Module-3 ├── Exercise-1 │ ├── Readme.md │ ├── images │ │ └── docker-compose.gif │ └── scripts │ │ └── docker-compose.yml ├── Exercise-2 │ ├── Readme.md │ └── images │ │ └── docker-compose-up.gif ├── Readme.md └── images │ └── topology.jpg ├── Readme.md └── images └── containers-visual.png /Module-0/Readme.md: -------------------------------------------------------------------------------- 1 | # Module 0: Access your Docker Lab Development Box 2 | 3 | If you decided not to follow along with this lab on your local host with docker pre-installed, you can gain access to your cloud hosted student development box for development. This VM comes pre-installed with Docker and all dependencies required to complete this lab. 4 | 5 | This module will be used at ServTech 2017 for TechTables. Cloud hosted resources will **only** available during the allotted Session time slots. 6 | 7 | # Accessing your VM 8 | 9 | 1. Navigate to [https://portal.sl.americasreadiness.com](https://portal.sl.americasreadiness.com) to access your HTML5 based VM console through [Guacamole](https://vimeo.com/116207678). 10 | 11 | 2. Logon using your username and password : 12 | * **Username:** User**X** 13 | * **Password:** XXXXXX (given out at the time of session) 14 | 15 | > **X** in 'UserX' corresponds to your student number (1 through 7). For example, if you are student 1, you will logon with username `user1` and password `Password01`. 16 | 17 | 3. You will enter directly into a CLI interface of an Ubuntu Docker host which you will use to learn more about docker. A few things to note about the HTML5 based console: 18 | 19 | * If you need to change input methods to copy and paste text into the CLI, press the keys: `alt ` + `ctrl` + `shift` to toggle the side pane. 20 | * You can select **None** which directly translates keyboard inputs into the CLI 21 | * You can select **Text Input** for a text form field entry to copy and paste large amounts of text. 22 | * You can also select **On-Screen Keyboard** if you are on a tablet or touch screen device without a proper keyboard input device. 23 | * Be sure to use the **Clipboard** text box on the left. If you would like to paste text into the console, somply **right click into the CLI screen**. 24 | 25 | # Demonstration 26 | 27 | ![Login to console](images/console-logon.gif) 28 | 29 | # Shortcuts 30 | 31 | 1. [Module 0-A: Install Docker Locally](https://hub.docker.com/?next=https%3A%2F%2Fhub.docker.com%2F) 32 | 2. [Module 1: Running Docker Containers](../Module-1) 33 | 3. [Module 2: Creating Custom Images from Dockerfiles](../Module-2) 34 | 4. [Module 3: Using Docker Compose](../Module-3) 35 | -------------------------------------------------------------------------------- /Module-0/images/console-logon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Citrix-TechSpecialist/Docker-101/45bb1b6284bba2de2518b8fb6049997f668ff8b9/Module-0/images/console-logon.gif -------------------------------------------------------------------------------- /Module-1/Exercise-1/Readme.md: -------------------------------------------------------------------------------- 1 | # Pulling from Docker Hub 2 | 3 | Before any container is run, a local copy of the image is always stored locally on the host. In this exercise we will pull a docker image from docker hub onto the docker host. 4 | 5 | ### Step 1 6 | 7 | To pull an image down locally onto a host, run the following command: 8 | 9 | `docker pull mayankt/webserver:a` 10 | 11 | Here is a break down of the command as follows: 12 | 13 | * `docker pull`: Is a docker command that tells docker engine to pull an image down from somewhere 14 | * `mayankt/webserver:a`: Is the image name. By default, if a full FQDN is not specified, it is assumed you are pulling the image from docker hub. In this case, you will be pulling an image from my repository [mayankt](https://hub.docker.com/r/mayankt/webserver/) with an image titled `webserver` with the tag of `a`. 15 | 16 | * Tagging images can help with versioning of your docker images and many other CI/CD use cases as well. 17 | 18 | Once you run the command you should see an output similar to this: 19 | 20 | ``` 21 | Pulling from mayankt/webserver 22 | 3ac0c2aa6889: Pull complete 23 | ec2ec713dc4f: Pull complete 24 | ea0a5af9851c: Pull complete 25 | 555bf6439b47: Pull complete 26 | 71080d75d6eb: Pull complete 27 | c787ac6d0b0a: Pull complete 28 | 1a9841bc3a47: Pull complete 29 | 1a7ce5d6010a: Pull complete 30 | eec46f0642a8: Pull complete 31 | d2d3a856c0da: Pull complete 32 | f128b2a739b4: Pull complete 33 | 1341f98ff817: Pull complete 34 | ``` 35 | Which indicates that the image is being pulled from docker hub locally onto your docker host. 36 | 37 | ### Step 2 38 | 39 | Type the following command to see a list of all images stored locally on your host. 40 | 41 | `docker images` 42 | 43 | Your output should resemble : 44 | 45 | ``` 46 | REPOSITORY TAG IMAGE ID CREATED SIZE 47 | mayankt/webserver a 18f05d0cd921 2 months ago 27.9MB 48 | ``` 49 | 50 | This output shows a 27.9 MB large docker image stored locally that can be run into instances of docker containers. All the `f128b2a739b4: Pull complete` outputs from the `docker pull` command above are the layers being pulled from Docker Hub of which constitute the `mayankt/webserver:a` docker image. 51 | 52 | ### Conclusion 53 | 54 | A simple `docker pull < image-name > ` commands shows how you can pull a docker image from the cloud directly. It's not used until you run a container with it which we will do in the [next Exercise](../Exercise-2). 55 | 56 | ![docker-pull](images/docker-pull.gif) 57 | 58 | ### Shortcuts 59 | 60 | 1. [Module 0-A: Install Docker Locally](https://hub.docker.com/?next=https%3A%2F%2Fhub.docker.com%2F) 61 | 2. [Module 0-B: Access your Docker Lab Development Box](../../Module-0) 62 | 2. [Module 1: Running Docker Containers](../../Module-1) 63 | 3. [Module 2: Creating Custom Images from Dockerfiles](../../Module-2) 64 | 4. [Module 3: Using Docker Compose](../../Module-3) 65 | -------------------------------------------------------------------------------- /Module-1/Exercise-1/images/docker-pull.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Citrix-TechSpecialist/Docker-101/45bb1b6284bba2de2518b8fb6049997f668ff8b9/Module-1/Exercise-1/images/docker-pull.gif -------------------------------------------------------------------------------- /Module-1/Exercise-2/Readme.md: -------------------------------------------------------------------------------- 1 | # Running a Container 2 | 3 | Lets convert a docker image into a running instance of a docker container to host a simple website. 4 | 5 | ### Step 1 6 | 7 | We will issue [`docker run`](https://docs.docker.com/engine/reference/run/) commands to run containers. Type the following command to host a website on the docker host on port `10000`. 8 | 9 | `docker run -dt --restart=always --name=cpx-blog -p 10000:80 mayankt/cpx-blog` 10 | 11 | Here is the breakdown of the command from above: 12 | 13 | * `docker run -dt` 14 | * This will run the container detached in the background. Later we will see [how we can attach to this container's terminal](https://stackoverflow.com/questions/30172605/how-to-get-into-a-docker-container), but for now we will have the container running detached in the background as a daemon. 15 | 16 | * `--restart=always` 17 | * This will restart the container automatically if it crashes or if and when docker/host restart. 18 | 19 | * `--name=cpx-blog` 20 | * This gives the container a name for more intuitive reference in later docker commands. Without a name parameter, the container will be randomly assigned a name and can be referenced to via the random name or the hash id of the container. 21 | 22 | * `-p 10000:80` 23 | * This will expose port `10000` on the host and map it to port `80` on the container for access to the hosted website. 24 | 25 | * `mayankt/cpx-blog` 26 | * This identifies the `latest` tagged image by default because no explicit tag is specified. It will be pulled from dockerhub to use when running the container. 27 | 28 | > It is not necessary to pull a desired image before executing a `docker run` command. If the image does not exist locally, it will be automatically pulled from the designated registry (Docker Hub in our case). 29 | 30 | Once you have entered the command, you will notice an output similar to below: 31 | 32 | ``` 33 | Unable to find image 'mayankt/cpx-blog:latest' locally 34 | latest: Pulling from mayankt/cpx-blog 35 | 3ac0c2aa6889: Already exists 36 | ec2ec713dc4f: Already exists 37 | ea0a5af9851c: Already exists 38 | 555bf6439b47: Already exists 39 | 71080d75d6eb: Already exists 40 | c787ac6d0b0a: Already exists 41 | 1a9841bc3a47: Already exists 42 | 336c032aec9b: Pull complete 43 | bc3b4209c6c5: Pull complete 44 | 3a5d33d6e1e0: Pull complete 45 | 7e846adb4c7d: Pull complete 46 | Digest: sha256:141100857249a391261edf7335ffea1ca20478a15d3ac08c821561e7a8998ef9 47 | Status: Downloaded newer image for mayankt/cpx-blog:latest 48 | 222bcc5747008a5c05f79d3a717f9132c8aa66234939677d3ebbcc5d883b5b5c 49 | ``` 50 | > Notice some of the layers already exist locally from our previous webserver-a pull because both images use mutual base images that can be recycled and save on local space. Only deltas or layers that make cpx-blog unique from webserver-a are pulled that are not stored locally. Also the output provided in the last line `222bcc5747008a5c05f79d3a717f9132c8aa66234939677d3ebbcc5d883b5b5c` is the long unique id of the running container that can be referenced in future docker commands alternative to `cpx-blog`. 51 | 52 | ### Step 2 53 | 54 | To see all of your running containers type the following command: 55 | 56 | `docker ps` 57 | 58 | That will yield an output similar to below. 59 | 60 | ``` 61 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 62 | 222bcc574700 mayankt/cpx-blog "/bin/sh -c 'nginx'" About a minute ago Up About a minute 443/tcp, 0.0.0.0:10000->80/tcp cpx-blog 63 | ``` 64 | 65 | This shows that the container named cpx-blog is running based on the image mayankt/cpx-blog with an external 10000 port exposed on the local host for remote access. Navigate to [`htt://localhost:10000`](http://localhost:1000) to see the website. 66 | 67 | > If you are following along in the sandbox environment, navigate to [`http://userX-lb.sl.americasreadiness.com`](http://userX-lb.sl.americasreadiness.com) which is a proxied VIP provided for you for remote access. Remember, X denotes your user number. 68 | 69 | To stop your running container, type the following command: 70 | 71 | `docker stop cpx-blog` 72 | 73 | Now to see all of your running and **non running** containers, type the following command: 74 | 75 | `docker ps -a` 76 | 77 | That will yield an output similar to below. 78 | 79 | ``` 80 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 81 | f407d06318f0 mayankt/cpx-blog "/bin/sh -c 'nginx'" 10 minutes ago Exited (137) 3 seconds ago cpx-blog 82 | ``` 83 | 84 | ### Step 3 85 | 86 | If you container is not running already, start the container with the following command: 87 | 88 | `docker start cpx-blog` 89 | 90 | Then type the following command to enter the terminal shell of the container: 91 | 92 | `docker exec -it cpx-blog /bin/sh` 93 | 94 | The breakdown of the command as follows: 95 | 96 | * `docker exec -it` 97 | * This will execute a command on the container with an interactive terminal (`-it`). We could execute commands in the background with a `-dt` if we wanted to, but we wouldn't see the outcome and the command we're running in this example is invoking a shell. If we wanted to run a script and not interact with it, we could have used the `-dt` flag. 98 | 99 | * `cpx-blog-b` 100 | * This denotes to which running container to execute the command against. In our case, it's the cpx-blog container. 101 | 102 | * `/bin/sh` 103 | * This is the command itself that we want to execute in the container. In this example we are invoking the shell for terminal access into our container. In other examples we could execute specific scripts or bash files as well (i.e. `/var/scripts/init-db.sh` or any other file locally stored within the container). 104 | 105 | After executing the command, you should have entered into the shell prompt of the container itself. Lets manipulate some files inside the container and see them reflected onto the website. 106 | 107 | Within the container shell, enter the following: 108 | 109 | `vi /var/wwww/index.html` 110 | > `vi` invokes a CLI text editor. It is functionally the same thing as notepad or sublime text, but geared for CLI interfaces not GUI interfaces. `vi` utility helps edit files in the terminal. 111 | 112 | Once you have opened the `index.html` file, scroll down and change the text between : 113 | 114 | `

NetScaler NITRO Blogs

` to anything you desire, such as `

LEARN DOCKER!

` 115 | 116 | > Edit text by entering the `i` key and then navigating the cursor to delete text you want to remove then typing in text you want to replace it with. Once completed, hit the `esc` key then `shift` + `:` key. Type `wq` to write and quit. You can then hit `ctrl` + `l` to clear the screen if you desire. 117 | 118 | Once back into the container's CLI, simply type `exit` to logout and return back to the host's terminal. 119 | 120 | Once the `index.html` is updated, refresh your browser or navigate back to [`http://localhost:1000`](http://localhost:1000) to observer changes to your site. 121 | > If you are following along in the sandbox environment, navigate to [`http://userX-lb.sl.americasreadiness.com`](http://userX-lb.sl.americasreadiness.com) which is a proxied VIP provided for you for remote access. Remember, X denotes your user number. 122 | 123 | ### Conclusion 124 | 125 | This concludes how to run a container with `docker run` command, how to attach to a container's terminal via the `docker exec -it` command, and how to manipulate data within a running container. 126 | 127 | ![docker-run](images/docker-run.gif) 128 | 129 | ### Shortcuts 130 | 131 | 1. [Module 0-A: Install Docker Locally](https://hub.docker.com/?next=https%3A%2F%2Fhub.docker.com%2F) 132 | 2. [Module 0-B: Access your Docker Lab Development Box](../../Module-0) 133 | 2. [Module 1: Running Docker Containers](../../Module-1) 134 | 3. [Module 2: Creating Custom Images from Dockerfiles](../../Module-2) 135 | 4. [Module 3: Using Docker Compose](../../Module-3) 136 | -------------------------------------------------------------------------------- /Module-1/Exercise-2/images/docker-run.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Citrix-TechSpecialist/Docker-101/45bb1b6284bba2de2518b8fb6049997f668ff8b9/Module-1/Exercise-2/images/docker-run.gif -------------------------------------------------------------------------------- /Module-1/Readme.md: -------------------------------------------------------------------------------- 1 | # Module 1: Running Docker Containers 2 | 3 | Once you have Docker installed locally or have gained access to the sandbox host, you can verify your installation by simply typing `docker --version` to see an output similar to: 4 | 5 | ``` 6 | Docker version 17.06.0-ce, build 02c1d87 7 | ``` 8 | > If you get a permissions errors on your local docker machine, type `sudo docker --version` to complete the task. The current user must be in [sudoers or docker group](https://docs.docker.com/engine/installation/linux/linux-postinstall/) to execute docker commands. 9 | 10 | ## Docker Images 11 | 12 | All Docker containers are based off [Docker images](https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/). Docker images are built up from a series of layers. Each layer represents an instruction or resulting block storage changes to the container's filesystem. Each layer except the very last one is read-only, so once an image is made, any changes to data in a running container are made on a separate R/W layer. Think of it as application layering where the first image layer is always a blank minimalistic starting block from [scratch](https://hub.docker.com/_/scratch/) and changes are done by installing dependencies or applications that you desire to package with your docker image. See the visual below to illustrate the concept of image layering: 13 | 14 | ![docker image layers](./images/docker-images.png) 15 | 16 | You can store docker images in several places: 17 | 18 | 1. [Docker Hub](https://hub.docker.com/explore/) 19 | 2. [Other Public Repositories](https://quay.io/tour/) 20 | 3. [Locally on your machine](http://blog.thoward37.me/articles/where-are-docker-images-stored/) 21 | 4. [Private Docker registries](https://docs.docker.com/registry/deploying/#storage-customization) 22 | 5. [In a tar archive](https://docs.docker.com/engine/reference/commandline/save/) 23 | 24 | We will mainly be concerning ourselves with **#1** pulling images from Docker Hub. 25 | 26 | ## Exercises 27 | 28 | Navigate to and complete the following exercises within **Module 1**. 29 | 30 | 1. [Pulling from Docker Hub](./Exercise-1) 31 | 2. [Running a Container](./Exercise-2) 32 | 33 | # Reset the Sandbox Environment 34 | 35 | Once you have completed **Module 1**, reset the environment by typing in the following command in your sandbox environment: 36 | 37 | `sudo /dockerclean.sh` 38 | 39 | If you are following along on your local machine, enter the following commands to remove all docker containers, images, and docker volumes from your host: 40 | 41 | ``` 42 | docker kill $(docker ps -q) 43 | docker rm -f $(docker ps -a -q) 44 | docker rmi $(docker images -q) 45 | docker volume rm $(docker volume ls -qf) 46 | ``` 47 | 48 | ### Shortcuts 49 | 50 | 1. [Module 0-A: Install Docker Locally](https://hub.docker.com/?next=https%3A%2F%2Fhub.docker.com%2F) 51 | 2. [Module 0-B: Access your Docker Lab Development Box](../Module-0) 52 | 2. [Module 1: Running Docker Containers](../Module-1) 53 | 3. [Module 2: Creating Custom Images from Dockerfiles](../Module-2) 54 | 4. [Module 3: Using Docker Compose](../Module-3) 55 | -------------------------------------------------------------------------------- /Module-1/images/docker-images.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Citrix-TechSpecialist/Docker-101/45bb1b6284bba2de2518b8fb6049997f668ff8b9/Module-1/images/docker-images.png -------------------------------------------------------------------------------- /Module-2/Exercise-1/Readme.md: -------------------------------------------------------------------------------- 1 | # Build a Docker Image 2 | 3 | In this exercise we will create a new docker image locally from a Dockerfile as discussed in [Module 2](../) overview. We will also make edits to the Dockerfile to customize the image to our preference. 4 | 5 | ### Step 1 6 | 7 | If you're following along in the sandbox environment, [logon to the console](../../Module-0) otherwise follow along on your local machine with docker installed. 8 | 9 | In the `/data` directory on your host, clone the following git repository: 10 | 11 | ``` 12 | # Change the working directory to /data on the docker host 13 | cd /data 14 | 15 | # Clone a copy of the github project locally 16 | sudo git clone https://github.com/Citrix-TechSpecialist/GoLang-cpx.git 17 | 18 | # List the contents in the /data directory to see the GoLang-cpx project directory 19 | ls -l 20 | ``` 21 | 22 | in the `/GoLang-cpx` directory there is a `Dockerfile`. Lets view the contents with the `cat` command. 23 | 24 | ``` 25 | # Change working directory into the GoLang-cpx project 26 | cd GoLang-cpx 27 | 28 | # View what all is in the GoLang-cpx directory 29 | ls -l 30 | 31 | # View the Dockerfile contents 32 | cat Dockerfile 33 | ``` 34 | 35 | Once you `cat` the file, you will notice the following content in the [Dockerfile](./scripts/Dockerfile): 36 | 37 | ``` 38 | FROM fnichol/uhttpd 39 | MAINTAINER Mayank Tahilramani and Brian Tannous 40 | COPY ./cpx-blog /www 41 | EXPOSE 80 42 | ENTRYPOINT /usr/sbin/run_uhttpd -f -p 80 -h /www 43 | CMD [""] 44 | ``` 45 | The content above is pretty much the same we observed in the [Module-2](../) overview. We will now build this container using the [`docker build`](https://docs.docker.com/engine/reference/commandline/build/) command. 46 | 47 | # Step 2 48 | 49 | Type the following command to build your docker image: 50 | 51 | ``` 52 | docker build -t cpx-blog . 53 | ``` 54 | 55 | Here is the breakdown of the command above: 56 | 57 | `docker build` basically tells the docker engine to build an image. 58 | 59 | `-t` give the image a name and optionally a tag in the `name:tag` format. 60 | 61 | `cpx-blog` will be the name of the created docker image. 62 | 63 | `.` tells docker engine to look for a file named *Dockerfile* (by default) in the current directory. 64 | 65 | You will see the following output: 66 | 67 | ``` 68 | Sending build context to Docker daemon 1.773MB 69 | Step 1/6 : FROM fnichol/uhttpd 70 | latest: Pulling from fnichol/uhttpd 71 | a3ed95caeb02: Pull complete 72 | 1775fca35fb6: Pull complete 73 | 718e21306e6b: Pull complete 74 | 889bfeab2d4e: Pull complete 75 | 8ac43f1732b7: Pull complete 76 | cefd08b5f834: Pull complete 77 | a32be2ed7953: Pull complete 78 | 1c78be7a5ec7: Pull complete 79 | 74984e6e6d1c: Pull complete 80 | Digest: sha256:28e6f95cf33ae1336525034e2b9d58ddf3cc63a2cdd9edebc8765321d96da9e0 81 | Status: Downloaded newer image for fnichol/uhttpd:latest 82 | ---> df0db1779d4d 83 | Step 2/6 : MAINTAINER Mayank Tahilramani and Brian Tannous 84 | ---> Running in 459db6d6a053 85 | ---> d51effaba5ae 86 | Removing intermediate container 459db6d6a053 87 | Step 3/6 : COPY ./cpx-blog /www 88 | ---> b1510a20020d 89 | Removing intermediate container b075c62a629b 90 | Step 4/6 : EXPOSE 80 91 | ---> Running in 84f5263cb817 92 | ---> f1f4672c9d5b 93 | Removing intermediate container 84f5263cb817 94 | Step 5/6 : ENTRYPOINT /usr/sbin/run_uhttpd -f -p 80 -h /www 95 | ---> Running in 8e2b276c3aa9 96 | ---> 552dfe1be7a7 97 | Removing intermediate container 8e2b276c3aa9 98 | Step 6/6 : CMD 99 | ---> Running in 7c5da4990bea 100 | ---> 0c78968bbe81 101 | Removing intermediate container 7c5da4990bea 102 | Successfully built 0c78968bbe81 103 | Successfully tagged cpx-blog:latest 104 | ``` 105 | 106 | You can also see the images on the local host with the following command: 107 | 108 | `docker images` 109 | 110 | which shows: 111 | 112 | ``` 113 | REPOSITORY TAG IMAGE ID CREATED SIZE 114 | cpx-blog latest 0c78968bbe81 46 seconds ago 5.66MB 115 | fnichol/uhttpd latest df0db1779d4d 3 years ago 4.87MB 116 | ``` 117 | Docker pulled the base image `fnichol/uhttpd` from Docker Hub as well as created the layer on top with the changes we added defined in our Dockerfile to create the `cpx-blog` image. This concludes the creation of the image. Next we will create *ONE* more image that is an updated version of the `cpx-blog` image before we run our containers in [Exercise-2](../Exercise-2). 118 | 119 | # Step 2 120 | 121 | Lets make updates to our `Dockerfile` in `/data/GoLang-cpx`. Open the file up in `nano` with the following command. `nano` is nothing more than a simple text editor for CLI. It can be considered an equivalent to [Sublime Text](https://www.sublimetext.com/) or [Notepad++](https://notepad-plus-plus.org/). 122 | 123 | ``` 124 | # In the /data/GoLang-cpx directory enter the following command: 125 | sudo nano Dockerfile 126 | ``` 127 | 128 | Update the file with your cursor and keyboard to reflect the following: 129 | 130 | ``` 131 | FROM fnichol/uhttpd 132 | MAINTAINER Mayank Tahilramani and Brian Tannous 133 | COPY ./cpx-blog /www 134 | WORKDIR /www 135 | RUN echo "find /www -type f -exec sed -i \"s/All rights reserved./Hosted by container: ${HOSTNAME}/g\" {} \\;" > /tmp/update.sh && chmod +x /tmp/update.sh 136 | EXPOSE 80 137 | ENTRYPOINT /tmp/update.sh && /usr/sbin/run_uhttpd -f -p 80 -h /www 138 | CMD [""] 139 | ``` 140 | >To exit `nano` after you are done editing, enter the keys `ctrl` + `x` then `y` and `enter` to save and quit. 141 | 142 | Here are the details on the updated command lines added above: 143 | 144 | ``` 145 | WORKDIR /www 146 | ``` 147 | 148 | This changes the working directory of the docker build engine when executing `RUN` commands in the next line. Note that you cannot simply change directories by a single `RUN` command for example `cd /www` because each time you execute a `RUN` command, docker spawns a new container and therefore the default working directory become `/`. See more context [here](https://stackoverflow.com/questions/17891981/docker-run-cd-does-not-work-as-expected) 149 | 150 | 151 | ``` 152 | RUN echo "find /www -type f -exec sed -i \"s/All rights reserved./Hosted by container: ${HOSTNAME}/g\" {} \\;" > /tmp/update.sh && chmod +x /tmp/update.sh 153 | ``` 154 | This command simply creates a script `/tmp/update.sh` which finds all files in the `/www` directory and replaces strings in them which match the pattern "*All rights reserved.*" with the string "*Hosted by container: ${HOSTNAME}*" where `${HOSTNAME}` is a built in environmental variable in the running container that holds the container's unique host name. By default the hostname of any container is it's short uuid. This script will essentially replace the footer of all web pages which state "All rights reserved" with which container is specifically hosting the website. 155 | 156 | >Note that this command creates a script `/tmp/update.sh` but does not execute it. This command needs to be executed in a final running container state, not during an intermediate step when building the final image. Hence this script created here is executed in the `ENTRYPOINT` step below. 157 | 158 | ``` 159 | ENTRYPOINT /tmp/update.sh && /usr/sbin/run_uhttpd -f -p 80 -h /www 160 | ``` 161 | This command dictates what to execute when the container starts. Note that the [container's lifespan](https://medium.com/@lherrera/life-and-death-of-a-container-146dfc62f808) is directly dependent on the service it runs on start, in our case first the `/tmp/update.sh` script executes to update footers on all html pages, then the httpd (found at `/usr/sbin/run_uhttpd`) is executed. The entrypoint script basically starts the webservice hosting content in `/www`. If for whatever reason the uhttpd service itself fails, hangs, or stops, the running container will stop as well. 162 | 163 | With the updated changes in our Dockerfile, we have introduced a new script into the container `/tmp/update.sh` which updated some HTML text on our website and is executed upon running the container as defined by our `ENTRYPOINT` statement. 164 | 165 | Now lets build our new image: 166 | 167 | ``` 168 | # In the /data/GoLang-cpx directory enter the following command: 169 | docker build -t cpx-blog:v2 . 170 | ``` 171 | View your images via the `docker images` command to see a new tagged version of the `cpx-blog` image was created. 172 | 173 | ``` 174 | WORKDIR /www 175 | REPOSITORY TAG IMAGE ID CREATED SIZE 176 | cpx-blog v2 7f4074f9eecf 9 seconds ago 5.66MB 177 | cpx-blog latest 0c78968bbe81 25 minutes ago 5.66MB 178 | fnichol/uhttpd latest df0db1779d4d 3 years ago 4.87MB 179 | ``` 180 | 181 | ### Conclusion 182 | 183 | In **Step 1** and **Step 2** we created 2 docker images of the same CPX-blog website. The first image was created using a Dockerfile form the [GoLang-cpx](https://github.com/Citrix-TechSpecialist/GoLang-cpx/) repository. The second image was created from a custom Dockerfile which added a script to update the footer of the website with the container's hostname. Below is a overview of the steps above. 184 | 185 | ![docker build](./images/docker-build.gif) 186 | 187 | Now lets move on to [Module-2: Exercise 2](../Exercise-2) to run the containers from the images we created. 188 | 189 | ### Shortcuts 190 | 191 | 1. [Module 0-A: Install Docker Locally](https://hub.docker.com/?next=https%3A%2F%2Fhub.docker.com%2F) 192 | 2. [Module 0-B: Access your Docker Lab Development Box](../../Module-0) 193 | 2. [Module 1: Running Docker Containers](../../Module-1) 194 | 3. [Module 2: Creating Custom Images from Dockerfiles](../../Module-2) 195 | 4. [Module 3: Using Docker Compose](../../Module-3) 196 | -------------------------------------------------------------------------------- /Module-2/Exercise-1/images/docker-build.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Citrix-TechSpecialist/Docker-101/45bb1b6284bba2de2518b8fb6049997f668ff8b9/Module-2/Exercise-1/images/docker-build.gif -------------------------------------------------------------------------------- /Module-2/Exercise-1/scripts/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM fnichol/uhttpd 2 | MAINTAINER Mayank Tahilramani and Brian Tannous 3 | COPY ./cpx-blog /www 4 | EXPOSE 80 5 | ENTRYPOINT /usr/sbin/run_uhttpd -f -p 80 -h /www 6 | CMD [""] 7 | -------------------------------------------------------------------------------- /Module-2/Exercise-2/Readme.md: -------------------------------------------------------------------------------- 1 | # Run a Docker Container 2 | 3 | In this exercise we will create a two docker containers from two new images we created in the [previous exercise](../Exercise-1). We will also create a third container that uses volume mounts to share persistent data with the docker host. 4 | 5 | ### Step 1 6 | 7 | Lets run our first container based off the image we created from the [GoLang-cpx](https://github.com/Citrix-TechSpecialist/GoLang-cpx/) repository. Enter the following command to run your docker container: 8 | 9 | `docker run -dt --name=cpx-blog-1 -p 10000:80 cpx-blog` 10 | 11 | Here is the breakdown of the command: 12 | 13 | * `docker run -dt` 14 | * This will run the container detached with a terminal in the background. Later we will see how we can attach to this container's CLI, but for now we will have the container running detached in the background as a daemon. 15 | 16 | * `--name=cpx-blog-1` 17 | * This gives the container a name for more intuitive reference in later docker commands. Without a name parameter, the container will be randomly assigned a name and can be referenced to via the random name or the hash id of the container. 18 | 19 | * `-p 10000:80` 20 | * This will expose port `10000` on the host and map it to port `80` on the container for access to the hosted website. 21 | 22 | * `cpx-blog` 23 | * This identifies the image we want to use by the docker engine to base our container off of. It will not bother pulling from Dockerhub because the image is already stored locally given you have completed [exercise 1](../Exercise-1). 24 | 25 | You should receive an output of a long UID as a reference to the running container similar to ` 26 | ed2348b56eda197a90313c8876ab4e6601b52406ba1c6740ccccd6e996565f60` 27 | 28 | You can view the running container by entering in the `docker ps` command: 29 | ``` 30 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 31 | ed2348b56eda cpx-blog "/bin/sh -c '/usr/..." About a minute ago Up About a minute 0.0.0.0:10000->80/tcp cpx-blog-1 32 | ``` 33 | 34 | Now lets view the website hosted by our docker container. If you are following along on your local machine, go to url [http://localhost:10000](http://localhost:10000). 35 | 36 | If you are following along in the sandbox environment, navigate your local browser to [http://userX-lb.sl.americasreadiness.com](http://userX-lb.sl.americasreadiness.com) where `X` denotes your user number in the FQDN. 37 | 38 | On the website, scroll down to the very bottom to notice the footer of this page stating: *2016. All rights reserved.* Make a note of this, because we are now going to run our new container that will have updated footer information. 39 | 40 | ![cpx-blog-1 site's footer](./images/cpx-blog-1-footer.png) 41 | 42 | Lastly, lets remove this container so we can recycle the host port `10000` for our new container. 43 | 44 | `docker rm -f cpx-blog-1` 45 | 46 | ### Step 2 47 | 48 | Lets run our second container based off the image we created from the modified version of the Dockerfile in the [GoLang-cpx](https://github.com/Citrix-TechSpecialist/GoLang-cpx/) repository. Enter the following command to run your docker container: 49 | 50 | `docker run -dt --name=cpx-blog-2 -p 10000:80 cpx-blog:v2` 51 | 52 | You can view the running container by entering in the `docker ps` command: 53 | ``` 54 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 55 | 6e115d1c21f5 cpx-blog:v2 "/bin/sh -c '/usr/..." About a minute ago Up About a minute 0.0.0.0:10000->80/tcp cpx-blog-2 56 | ``` 57 | 58 | Now lets view the updated website hosted by our new docker container. If you are following along on your local machine, go to url [http://localhost:10000](http://localhost:10000). 59 | 60 | If you are following along in the sandbox environment, navigate your local browser to [http://userX-lb.sl.americasreadiness.com](http://userX-lb.sl.americasreadiness.com) where `X` denotes your user number in the FQDN. 61 | 62 | On the website, scroll down to the very bottom and notice the footer of this page stating: *2016. Hosted by container: **11ad31695df3**.* The container hostname is showing up because of the `/tmp/update.sh` script that was executed when the container was run to update all `.html` footer code as defined in our new Dockerfile. 63 | 64 | ![cpx-blog-2 site's footer](./images/cpx-blog-2-footer.png) 65 | 66 | Lastly, lets remove this container so we can recycle the host port `10000` for our new container. 67 | 68 | `docker rm -f cpx-blog-2` 69 | 70 | ### Step 3 71 | 72 | Thus far we have seen the docker container host the website with the `.html` data local to it's file system. Ideally, you would not want to store any persistent data on the container itself, rather you should store in on some network or hyperconverged storage solution that the container can access as if local instead. This allow you to de-coupling storage of persistent data in the container from the compute processing done by the container itself and allows you to be more agile, spreads your failure domain, and scale independently in storage and compute capacity. 73 | 74 | In this In this step, we will run the same container as in **step 1**, but with a volume mount that shares a directory with the docker host mounted in the container to share persistent data. Enter the following command to run a docker container with a [volume mount](https://docs.docker.com/engine/reference/run/#volume-shared-filesystems). 75 | 76 | `docker run -dt --name=cpx-blog-3 -p 10000:80 -v /data/GoLang-cpx/cpx-blog:/www:rw cpx-blog` 77 | 78 | Here is the breakdown of the new volume mount `-v` flag in the `docker run` command: 79 | 80 | * `-v /data/GoLang-cpx/cpx-blog:/www:rw` 81 | * The `-v` flag denotes that this container will have a volume mount that is located on the local host at `/data/GoLang-cpx/cpx-blog` and that directory will be mapped to the `/www` directory that is local to the container. The container will have *read/write* permissions to this directory as denoted by the `:rw` at the end. 82 | 83 | * This allows us to remove the `COPY ./cpx-blog /www` command form our Dockerfile if desired to. However the pre-requisite of this container would become that a volume mount be provided at run time to host whatever content is in the mounted `/www` directory local to the container.. 84 | 85 | * Adding volume mounts of persistent data to containers saves space as well, because now the data isn't replicated in each container, rather multiple containers can instead reference the same volume mount on a NFS network share, for example, mounted on the local docker host. 86 | 87 | * Other services can also independently manipulate data in the volume mount directory on the host that have read-write access and it can be reflected in the running cpx-blog containers for example. 88 | 89 | Once you have your container running, lets view the site hosted by our new docker container. If you are following along on your local machine, go to url [http://localhost:10000](http://localhost:10000). 90 | 91 | If you are following along in the sandbox environment, navigate your local browser to [http://userX-lb.sl.americasreadiness.com](http://userX-lb.sl.americasreadiness.com) where `X` denotes your user number in the FQDN. 92 | 93 | You will notice that the site looks identical to it did in **Step 1**. Lets change some content to the title page to prove a point. 94 | 95 | Enter the following command to edit text in the `index.html` of the home page of the blog: 96 | 97 | `sudo nano /data/GoLang-cpx/cpx-blog/index.html` 98 | 99 | Scroll down into the file where you see the line: 100 | 101 | ``` 102 |

NetScaler NITRO Blogs

103 | ``` 104 | 105 | update that line to look like : 106 | 107 | ``` 108 |

LEARN DOCKER!

109 | ``` 110 | 111 | Save and quit `nano` by entering the keys `ctrl` + `x` then `y` and `enter`. 112 | 113 | Now refresh your browser to the blog to view the updates changes. You should see a new title in the home page reflecting your changes to the `index.html` 114 | 115 | Lastly, lets remove this container so we can recycle the host port `10000` for subsequent Modules. 116 | 117 | `docker rm -f cpx-blog-3` 118 | 119 | ### Conclusion 120 | 121 | In this module, we ran a container hosting our website using a Dockerfile in the [GoLang-cpx](https://github.com/Citrix-TechSpecialist/GoLang-cpx/) repository. We also ran a container that has a script that ran at runtime to dynamically update the footer on each webpage to display the container's hostname. Lastly, we deployed a third container that hosted the webpage through a volume mount where the data of the website only resided on the host and we showed that the data could be independently manipulated to reflect updates on our container hosted website. 122 | 123 | Here is an overview of the previous 3 steps. 124 | 125 | ![docker run 3 containers](./images/docker-run-new.gif) 126 | 127 | ### Shortcuts 128 | 129 | 1. [Module 0-A: Install Docker Locally](https://hub.docker.com/?next=https%3A%2F%2Fhub.docker.com%2F) 130 | 2. [Module 0-B: Access your Docker Lab Development Box](../../Module-0) 131 | 2. [Module 1: Running Docker Containers](../../Module-1) 132 | 3. [Module 2: Creating Custom Images from Dockerfiles](../../Module-2) 133 | 4. [Module 3: Using Docker Compose](../../Module-3) 134 | -------------------------------------------------------------------------------- /Module-2/Exercise-2/images/cpx-blog-1-footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Citrix-TechSpecialist/Docker-101/45bb1b6284bba2de2518b8fb6049997f668ff8b9/Module-2/Exercise-2/images/cpx-blog-1-footer.png -------------------------------------------------------------------------------- /Module-2/Exercise-2/images/cpx-blog-2-footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Citrix-TechSpecialist/Docker-101/45bb1b6284bba2de2518b8fb6049997f668ff8b9/Module-2/Exercise-2/images/cpx-blog-2-footer.png -------------------------------------------------------------------------------- /Module-2/Exercise-2/images/docker-run-new.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Citrix-TechSpecialist/Docker-101/45bb1b6284bba2de2518b8fb6049997f668ff8b9/Module-2/Exercise-2/images/docker-run-new.gif -------------------------------------------------------------------------------- /Module-2/Readme.md: -------------------------------------------------------------------------------- 1 | # Module 2: Create an Image from a Dockerfile 2 | 3 | Given that all Docker containers are based off specific [Docker images](https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/), in this module we will explore one of many ways to create Docker Images that define your custom docker containers. The following list a few ways to create customer images docker images: 4 | 5 | 1. [Save a running container into an image](https://docs.docker.com/engine/reference/commandline/save/) 6 | 7 | * In this method, users initially run a docker container based on a desired base image. Then after changes are made by `docker exec -it` or other means on the running container, the live container is ultimately saved into a `.tar` archive in its desired state that can be later [loaded](https://docs.docker.com/engine/reference/commandline/load/) to run multiple new instances of the custom image. 8 | 9 | 2. Defining a [Dockerfile](https://www.digitalocean.com/community/tutorials/docker-explained-using-dockerfiles-to-automate-building-of-images) 10 | 11 | * This method is the most robust and more commonly use method to define and create docker images. A Dockerfile is essentially a recipe of commands to execute on a defined base image that constitute the desired state of a custom docker image. Consider the traditional workflow to configure a webserver which requires executing of scripts, updating local packages, pulling of code from various repositories, installing dependencies, etc. before the webserver is in its desired state. A Dockerfile can define those steps towards a desired state thus allowing changes to be made independently on external packages, code repositories, etc. and the Dockerfile will simply execute those commands and operations when you desire to build your custom container image. 12 | 13 | ### Dockerfiles 14 | 15 | The advantage of a Dockerfile over just storing the binary image (or a snapshot/template in other virtualization systems) is that the automatic builds will ensure you have the latest version of code, packages, and external resources available in your docker container. This is a good thing from a security perspective, as you want to ensure you’re not installing any vulnerable software. This is also a good thing from an operational perspective because it allows you to rapidly build out isolated environments based on defined recipe that can pull from external resources like git to compile and build microservices. 16 | 17 | Below is an example of a simple [Dockerfile](./Exercise-1/scripts/Dockerfile): 18 | 19 | ``` 20 | FROM fnichol/uhttpd 21 | MAINTAINER Mayank Tahilramani and Brian Tannous 22 | COPY ./cpx-blog /www 23 | EXPOSE 80 24 | ENTRYPOINT /usr/sbin/run_uhttpd -f -p 80 -h /www 25 | CMD [""] 26 | ``` 27 | Breakdown of details below: 28 | 29 | ``` 30 | FROM fnichol/uhttpd 31 | ``` 32 | 33 | This denotes the base image to use. In this case, it's an image from Docker Hub of user [`fnichol`](https://github.com/fnichol/docker-uhttpd) who has already made a bare bone minimalistic docker image with the service [httpd](https://httpd.apache.org/docs/2.4/programs/httpd.html) pre-installed which allows us to hosts websites. All we have to do is provide our HTML code and relevant data. From this image, we will make changes and define our custom image based on subsequent commands in our Dockerfile. 34 | 35 | ``` 36 | MAINTAINER Mayank Tahilramani and Brian Tannous 37 | ``` 38 | 39 | This is just meta data for the image on who the maintainer/creator of the image and Dockerfile are. 40 | 41 | ``` 42 | COPY ./cpx-blog /www 43 | ``` 44 | 45 | This command simply copies everything `cpx-blog` directory that is local to the Dockerfile into the `/www` directory that is local to the container. Within the container there must already be a `/www` directory (as specified by the [base image](https://github.com/fnichol/docker-uhttpd) to put content in. In this case, any html code or data that will be served by httpd must reside in the `/www` directory within the container. 46 | 47 | ``` 48 | EXPOSE 80 49 | ``` 50 | 51 | This command simply states that port 80 will be open on the container as expected to host a website. 52 | 53 | ``` 54 | ENTRYPOINT /usr/sbin/run_uhttpd -f -p 80 -h /www 55 | ``` 56 | 57 | This command dictates what to execute when the container starts. Note that the container's lifespan is directly dependent on the service it runs on start, in our case the httpd (found at `/usr/sbin/run_uhttpd`) is executed. The entrypoint script basically starts the webservice `httpd` hosting content in `/www`. If for whatever reason the uhttpd service itself fails, hangs, or stops, the running container will stop running as well. 58 | 59 | `CMD [""]` 60 | 61 | This command is similar to ENTRYPOINT where traditionally you would define the default command to execute when the container starts. In this case, our ENTRYPOINT script is handling that for us so CMD can be left empty. 62 | * CMD is a mandatory declaration in a Dockerfile. 63 | 64 | ### Exercises 65 | 66 | Navigate to and complete the following exercises within Module 2: 67 | 68 | 1. [Build a Docker Image](./Exercise-1) 69 | 2. [Running the Container](./Exercise-2) 70 | 71 | ## Reset the Sandbox Environment 72 | 73 | Once completed Module 1, reset the environment by typing in the following command in your sandbox environment: 74 | 75 | `sudo /dockerclean.sh` 76 | 77 | If you are following along on your local machine, enter the following commands to remove all docker containers, images, and docker volumes from your host: 78 | 79 | ``` 80 | docker kill $(docker ps -q) 81 | docker rm -f $(docker ps -a -q) 82 | docker rmi $(docker images -q) 83 | docker volume rm $(docker volume ls -qf) 84 | ``` 85 | 86 | ### Shortcuts 87 | 88 | 1. [Module 0-A: Install Docker Locally](https://hub.docker.com/?next=https%3A%2F%2Fhub.docker.com%2F) 89 | 2. [Module 0-B: Access your Docker Lab Development Box](../Module-0) 90 | 2. [Module 1: Running Docker Containers](../Module-1) 91 | 3. [Module 2: Creating Custom Images from Dockerfiles](../Module-2) 92 | 4. [Module 3: Using Docker Compose](../Module-3) 93 | -------------------------------------------------------------------------------- /Module-3/Exercise-1/Readme.md: -------------------------------------------------------------------------------- 1 | # Create a docker-compose.yaml File 2 | 3 | Instead of creating a `docker-compose.yml` file from scratch, we are going to copy one from another repository to get started. We will then examine the file and understand it's anatomy before finally making edits to suite our environment needs. 4 | 5 | ### Step 1 6 | 7 | To get started, enter the following commands to clone a repository with a `docker-compose.yml` file already made for us. Navigate to the directory and view the contents with `nano`. 8 | 9 | ``` 10 | # Change directory to the workspace you want to clone the repository 11 | cd /data 12 | 13 | # Clone the desired repository 14 | sudo git clone https://github.com/Citrix-TechSpecialist/nitro-ide.git 15 | 16 | # enter the directory of the repository 17 | cd nitro-ide 18 | 19 | # View the file contents of docker-compose.yml 20 | nano docker-compose.yml 21 | ``` 22 | Here is a copy of the [docker-compose.yml](./scripts/docker-compose.yml) file for reference. It is recommended you open it in another tab in your browser to follow along. 23 | 24 | Below are the desired services we want to configure and deploy. 25 | 26 | 1. [Webserver A](https://hub.docker.com/r/mayankt/webserver/) that is a static website site 27 | 2. [Webserver B](https://hub.docker.com/r/mayankt/webserver/) that is a different static website 28 | 3. [Cloud9 IDE](https://c9.io/) which we will use to write code and execute python scripts to automate configuration of NetScaler CPX. 29 | 4. [NetScaler CPX](https://microloadbalancer.com) a NetScaler in a docker container that share the same API as other NetScaler ADCs. 30 | 31 | ### Explaining the docker-compose.yml File 32 | 33 | Below are snippets of the `docker-compose.yml` with comments (`#`) per line with details of the `key` : `value` pairs describing the desired deployment. 34 | 35 | ### Sandbox Network 36 | 37 | With Docker you can define specific container networks. In this case we are creating a [bridge network](https://docs.docker.com/engine/userguide/networking/#bridge-networks) specific to deploying only our desired containers to within a SDN boundary internal to the host. 38 | 39 | ``` 40 | networks: # This defines that below are settings for docker networks 41 | sandbox: # Name of the network 42 | driver: bridge # The type of network driver to use 43 | ipam: # Details of the network and IP space 44 | config: # Configuration parameters 45 | - subnet: "192.168.13.0/24" # The desired subnet of the docker network 46 | ``` 47 | 48 | 49 | ### WebServer A / B 50 | 51 | ``` 52 | webserver-a/b: # Service name 53 | image: "mayankt/webserver:a" # Docker container image to use 54 | restart: always # Restart the service if it fails or the host reboots 55 | networks: # This describes the docker networks the containers will be part of 56 | sandbox: # Docker network's name 57 | ipv4_address: "192.168.13.11" # Static IP address of this service/container. You can leave this key:value out and to obtain an IP from Docker's IPAM 58 | hostname: webserver-a # Desired hostname of the container 59 | ``` 60 | 61 | ### [NetScaler CPX](http://docs.citrix.com/en-us/netscaler-cpx/12/deploy-using-docker-image-file.html) 62 | 63 | ``` 64 | cpx: # Service Name 65 | image: "store/citrix/netscalercpx:12.0-41.16" # Docker container image to use from Citrix' registry 66 | environment: # Environment Variables local to the container 67 | EULA: "yes" # same as 'export EULA="yes"' as a pre-req for CPX to work 68 | restart: always # Restart the service if it fails or the host reboots 69 | cap_add: # Add specific container kernel capabilities https://docs.docker.com/engine/security/security/#linux-kernel-capabilities 70 | - NET_ADMIN # Perform various network-related operations https://linux.die.net/man/7/capabilities 71 | ulimits # Override the default (resource) ulimits for a container 72 | core: -1 # Use unlimited CPU, up to the amount available on the host system. 73 | networks: # This describes the docker networks the containers will be part of 74 | sandbox: # Docker network's name 75 | ipv4_address: "192.168.13.20" # Static IP address of this service/container. You can leave this key:value out and to obtain an IP from Docker's IPAM 76 | ports: # Exposed ports mapped to the host from the container. 77 | - "10000-10050:10000-10050" 78 | - "9080:80" 79 | hostname: ns-adc # Desired hostname of the container 80 | ``` 81 | 82 | ### Cloud9 IDE 83 | 84 | ``` 85 | nitro-ide: # Service Name 86 | image: "mayankt/nitro-ide" # Docker container image to use from Citrix' registry 87 | restart: always # Restart the service if it fails or the host reboots 88 | dns: 8.8.8.8 # Specific DNS server to use for host name resolution from within the container 89 | networks: # This describes the docker networks the containers will be part of 90 | sandbox: # Docker network's name 91 | ipv4_address: "192.168.13.10" # Static IP address of this service/ 92 | ports: # Exposed ports mapped to the host from the container. 93 | - "9090:80" 94 | - "9091:8000" 95 | links: # Link to containers in another service given service name and/or a link alias ("SERVICE:ALIAS"). "ping web-a" will ping the webserver-a service from within the container. 96 | - "cpx" 97 | - "webserver-a:web-a" 98 | - "webserver-b:web-b" 99 | volumes: # Volume mounts local to the host mapped to a directory local to the container with read/write access (rw) 100 | - ${DATA_DIR}:/workspace:rw 101 | hostname: nitro-ide # Desired hostname of the container 102 | ``` 103 | > Note you may have to uncomment the `volumes` section to mount volumes in the docker file that is pulled from the repository. Use `nano` to remove the `#` from the `volumes:` block. 104 | 105 | ### Step 2 106 | 107 | Set the environmental variable `DATA_DIR` to `/data` on the docker host. This environment variable will substitute the value `/data` into the docker compose file when we provision our containers. Type the following on your docker host: 108 | 109 | ``` 110 | export DATA_DIR="/data"` 111 | ``` 112 | 113 | Verify that the environmental variable was set successfully by typing the following command: 114 | 115 | ``` 116 | echo $DATA_DIR 117 | ``` 118 | 119 | It should return the `/data` directory path. 120 | 121 | ### Conclusion 122 | 123 | In this module we clones a repository with our desired compose file. We explored what constitutes a `docker-compose.yml` file and what the various parameters mean. We set the value `/data` for a placeholder in the compose file that took in an environment variable to specify which local directory will be mapped to our IDE's local workspace `/workspace` so we can share data from host to container. 124 | 125 | Here is an overview of configuration steps: 126 | 127 | ![docker-compose.yml](./images/docker-compose.gif) 128 | 129 | Now continue on to [Exercise-2](../Exercise-2) to compose your docker environment. 130 | 131 | ### Shortcuts 132 | 133 | 1. [Module 0-A: Install Docker Locally](https://hub.docker.com/?next=https%3A%2F%2Fhub.docker.com%2F) 134 | 2. [Module 0-B: Access your Docker Lab Development Box](../../Module-0) 135 | 2. [Module 1: Running Docker Containers](../../Module-1) 136 | 3. [Module 2: Creating Custom Images from Dockerfiles](../../Module-2) 137 | 4. [Module 3: Using Docker Compose](../../Module-3) 138 | -------------------------------------------------------------------------------- /Module-3/Exercise-1/images/docker-compose.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Citrix-TechSpecialist/Docker-101/45bb1b6284bba2de2518b8fb6049997f668ff8b9/Module-3/Exercise-1/images/docker-compose.gif -------------------------------------------------------------------------------- /Module-3/Exercise-1/scripts/docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "2" 3 | services: 4 | 5 | # WebServer-A 6 | webserver-a: 7 | image: "mayankt/webserver:a" 8 | restart: always 9 | networks: 10 | sandbox: 11 | ipv4_address: "192.168.13.11" 12 | hostname: webserver-a 13 | 14 | # WebServer-B 15 | webserver-b: 16 | image: "mayankt/webserver:b" 17 | restart: always 18 | networks: 19 | sandbox: 20 | ipv4_address: "192.168.13.12" 21 | hostname: webserver-b 22 | 23 | # NetScalet CPX 24 | cpx: 25 | image: "store/citrix/netscalercpx:12.0-41.16" 26 | environment: 27 | EULA: "yes" 28 | restart: always 29 | cap_add: 30 | - NET_ADMIN 31 | ulimits: 32 | core: -1 33 | networks: 34 | sandbox: 35 | ipv4_address: "192.168.13.20" 36 | ports: 37 | - "10000-10050:10000-10050" 38 | - "9080:80" 39 | hostname: ns-adc 40 | 41 | # IDE Environment 42 | nitro-ide: 43 | image: "mayankt/nitro-ide" 44 | restart: always 45 | dns: 8.8.8.8 46 | networks: 47 | sandbox: 48 | ipv4_address: "192.168.13.10" 49 | ports: 50 | - "9090:80" 51 | - "9091:8000" 52 | # Update the next two lines to map to your local host directory where your git projects are stored. 53 | volumes: 54 | - ${DATA_DIR}:/workspace:rw 55 | hostname: nitro-ide 56 | 57 | networks: 58 | # defined sandbox network for docker containers 59 | sandbox: 60 | driver: bridge 61 | ipam: 62 | config: 63 | - subnet: "192.168.13.0/24" -------------------------------------------------------------------------------- /Module-3/Exercise-2/Readme.md: -------------------------------------------------------------------------------- 1 | # Compose an Environment 2 | 3 | Once you have your [docker-compose.yml](../Exercise-1/scripts/docker-compose.yml) set, you can move forward with provisioning your environment. 4 | 5 | ### Step 1 6 | 7 | In the `/data/nitro-ide` directory, enter the following commands: 8 | 9 | ``` 10 | # Navigate to the repository local to your host 11 | cd /data/nitro-ide 12 | 13 | # Issue the docker compose command to provision your environment 14 | docker-compose up -d 15 | ``` 16 | > The `-d` in the [`docker-compose up -d`](https://docs.docker.com/compose/reference/up/) specifies that containers run in the background in detached mode. 17 | 18 | You should observe an output similar to the following: 19 | 20 | ``` 21 | Pulling cpx (store/citrix/netscalercpx:12.0-41.16)... 22 | 12.0-41.16: Pulling from store/citrix/netscalercpx 23 | 4e1f679e8ab4: Pull complete 24 | .. 25 | .. 26 | .. 27 | 588f5003e10f: Pull complete 28 | Digest: sha256:31a65cfa38833c747721c6fbc142faec6051e5f7b567d8b212d912b69b4f1ebe 29 | Status: Downloaded newer image for store/citrix/netscalercpx:12.0-41.16 30 | Pulling nitro-ide (mayankt/nitro-ide:latest)... 31 | latest: Pulling from mayankt/nitro-ide 32 | a3ed95caeb02: Pull complete 33 | .. 34 | .. 35 | .. 36 | 9581fa7fd579: Pull complete 37 | Digest: sha256:53c464876633e95f8e11ea821c50add0ff8e00a70c5aacd65f465d2d3045d8d3 38 | Status: Downloaded newer image for mayankt/nitro-ide:latest 39 | Pulling webserver-b (mayankt/webserver:b)... 40 | b: Pulling from mayankt/webserver 41 | 3ac0c2aa6889: Pull complete 42 | .. 43 | .. 44 | .. 45 | 4484f1613730: Pull complete 46 | Digest: sha256:5807d78ba9c3892238a1eef2763c82f719d077b02a0c087122b816d276f0fbc4 47 | Status: Downloaded newer image for mayankt/webserver:b 48 | Pulling webserver-a (mayankt/webserver:a)... 49 | a: Pulling from mayankt/webserver 50 | 3ac0c2aa6889: Already exists 51 | .. 52 | .. 53 | .. 54 | f128b2a739b4: Already exists 55 | 1341f98ff817: Pull complete 56 | Digest: sha256:921d4054855c335dcd48a83bd881fa9059fa003f62f1b29bbe4b3a40fc79cc9a 57 | Status: Downloaded newer image for mayankt/webserver:a 58 | Creating nitroide_webserver-b_1 59 | Creating nitroide_nitro-ide_1 60 | Creating nitroide_cpx_1 61 | Creating nitroide_webserver-a_1 62 | ``` 63 | You can validate your desired containers are running by issuing a `docker ps` command to see all running containers. 64 | 65 | ``` 66 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS 67 | NAMES 68 | 37892600b3d6 store/citrix/netscalercpx:12.0-41.16 "/bin/sh -c 'bash ..." 13 seconds ago Up 10 seconds 22/tcp, 443/tcp, 1 69 | 61/udp, 0.0.0.0:10000-10050->10000-10050/tcp, 0.0.0.0:9080->80/tcp nitroide_cpx_1 70 | 772b633440d7 mayankt/webserver:a "/bin/sh -c 'nginx'" 13 seconds ago Up 10 seconds 80/tcp, 443/tcp 71 | nitroide_webserver-a_1 72 | aeef73f08b84 mayankt/nitro-ide "supervisord -c /e..." 13 seconds ago Up 11 seconds 3000/tcp, 0.0.0.0: 73 | 9090->80/tcp, 0.0.0.0:9091->8000/tcp nitroide_nitro-ide_1 74 | bb50c29a35c8 mayankt/webserver:b "/bin/sh -c 'nginx'" 13 seconds ago Up 10 seconds 80/tcp, 443/tcp 75 | nitroide_webserver-b_1 76 | ``` 77 | 78 | ### Step 2 79 | 80 | Once all your containers are running successfully, navigate to your IDE's web console. If you are following along on your local machine, go to url [http://localhost:9090](http://localhost:9090). 81 | 82 | If you are following along in the sandbox environment, navigate your local browser to [http://userX-ide.sl.americasreadiness.com](http://userX-ide.sl.americasreadiness.com) where `X` denotes your user number in the FQDN. 83 | 84 | >Please wait up to 2 minutes for the IDE and CPX to fully load before they are accessible via the web console. Usually services are available within 30 seconds of deployment. 85 | 86 | You should be greeted with Cloud9's loading page and then ultimately the IDE editor pane. Within the side pane you should notice your `workspace` directory and within that directory you should see the `nitro-ide` repository on your docker host. 87 | 88 | You can select any file to open and edit it or to examine it. You even have access to the container's CLI terminal in the bottom pane. In the container's CLI pane within Cloud9 IDE, enter the following commands: 89 | 90 | ``` 91 | git clone -b cpx-101 https://github.com/Citrix-TechSpecialist/NetScalerNITRO.git 92 | ``` 93 | 94 | ### Step 3 95 | 96 | A new directory will have been created `NetScalerNITRO` with the `nsAuto.py` python script that is pre-coded to configure the CPX to loadbalance webserver-a and webserver-b. 97 | 98 | In the bottom pane within the container's CLI, enter the following commands to configure the CPX via NITRO scripted with Netscaler's Python SDK. Desired state configuration is specified in the `nsAutoCfg.json` file with pre-seeded default values for our environment (i.e. backend webserver IP's and CPX default username and pass along with its NSIP.) 99 | 100 | >It is highly encouraged to open the `nsAuto.py` and `nsAutoCfg.json` file within the IDE to examine and learn from its contents and understand how the script is coded with NetScaler's NITRO Python SDK. 101 | 102 | ``` 103 | cd NetScalerNITRO 104 | python nsAuto.py 105 | ``` 106 | 107 | You will see an output similar to: 108 | 109 | ``` 110 | Configuring NS 111 | Starting to configure... 112 | All done preforming configuration 113 | ``` 114 | 115 | This indicates that the CPX has been configured successfully. It is load balancing Webserver A and Webserver B on its port 10000, using it's docker container IP in the sandbox docker network. Container port 10000 is mapped to host port 10000 so you can access your load balancer at [http://localhost:10000](http://localhost:10000) if you are following along in the lab locally. 116 | 117 | If you are following along in the sandbox environment, navigate your local browser to [http://userX-lb.sl.americasreadiness.com](http://userX-lb.sl.americasreadiness.com) where `X` denotes your user number in the FQDN. 118 | 119 | # Step 3 120 | 121 | To validate the configurations on the NetScaler CPX, enter the following commands on the Docker host to attach to the container's bash terminal: 122 | 123 | `docker exec -it nitroide_cpx_1 /bin/bash` and you will have entered into CPX's CLI. 124 | 125 | Then enter in the following NetScaler CLI commands to view configured vservers on the ADC with the following command: 126 | 127 | `cli_script.sh "sh lb vservers"` and you will see an output similar to the following for the configured vserver: 128 | 129 | ``` 130 | 1)webserver (192.168.13.20:10000) - HTTPType: ADDRESS 131 | State: UP 132 | Last state change was at Fri Jul 14 02:02:23 2017 133 | Time since last state change: 0 days, 01:30:54.410 134 | Effective State: UP 135 | Client Idle Timeout: 180 sec 136 | Down state flush: ENABLED 137 | Disable Primary Vserver On Down : DISABLED 138 | Appflow logging: ENABLED 139 | Port Rewrite : DISABLED 140 | No. of Bound Services : 2 (Total) 2 (Active) 141 | Configured Method: ROUNDROBINBackupMethod: NONE 142 | Mode: IP 143 | Persistence: NONE 144 | Vserver IP and Port insertion: OFF 145 | Push: DISABLEDPush VServer: 146 | Push Multi Clients: NO 147 | Push Label Rule: none 148 | L2Conn: OFF 149 | Skip Persistency: None 150 | Listen Policy: NONE 151 | IcmpResponse: PASSIVE 152 | RHIstate: PASSIVE 153 | New Service Startup Request Rate: 0 PER_SECOND, Increment Interval: 0 154 | Mac mode Retain Vlan: DISABLED 155 | DBS_LB: DISABLED 156 | Process Local: DISABLED 157 | Traffic Domain: 0 158 | TROFS Persistence honored: ENABLED 159 | Retain Connections on Cluster: NO 160 | ``` 161 | 162 | ### Conclusion 163 | 164 | In this exercise we deployed a sandbox development environment with an IDE, NetScaler CPX, and 2 simple webservers using docker compose. We then logged into the IDE and cloned a repository with python code that will automatically configure the NetScaler CPX using the pre-defined input file `nsAutoCfg.json` that provides details on the desired configuration state of the CPX. We validated that the websites were being load balanced and saw the running load balancer configuration on the CPX. 165 | 166 | Here is an overview of the procedures above: 167 | 168 | ![docker compose up](./images/docker-compose-up.gif) 169 | 170 | ### Shortcuts 171 | 172 | 1. [Module 0-A: Install Docker Locally](https://hub.docker.com/?next=https%3A%2F%2Fhub.docker.com%2F) 173 | 2. [Module 0-B: Access your Docker Lab Development Box](../../Module-0) 174 | 2. [Module 1: Running Docker Containers](../../Module-1) 175 | 3. [Module 2: Creating Custom Images from Dockerfiles](../../Module-2) 176 | 4. [Module 3: Using Docker Compose](../../Module-3) 177 | -------------------------------------------------------------------------------- /Module-3/Exercise-2/images/docker-compose-up.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Citrix-TechSpecialist/Docker-101/45bb1b6284bba2de2518b8fb6049997f668ff8b9/Module-3/Exercise-2/images/docker-compose-up.gif -------------------------------------------------------------------------------- /Module-3/Readme.md: -------------------------------------------------------------------------------- 1 | # Module 3: Using Docker Compose 2 | 3 | By this point, it is assumed you have completed [Module 1](../Module-1) and [Module 2](../Module-2) and are familiar with basic Docker commands such as [`docker run`](https://docs.docker.com/engine/reference/run/#volume-shared-filesystems), [`docker ps`](https://docs.docker.com/engine/reference/commandline/ps/), [`docker pull`](https://docs.docker.com/engine/reference/commandline/pull/), [`docker rm`](https://docs.docker.com/engine/reference/commandline/rm/), and the various parameter flags (such as `-v` for volume mounts) associated with some of these commands. 4 | 5 | It should also be obvious at this point that deploying docker containers at scale by hand with `docker run` commands can be very involved and, at time, too complicated with multiple lines of `docker ..` commands to deploy a large environment. Luckily, docker containers are not meant to be deployed via individual commands, rather they are often deployed to a desired state using various other tools that help automate and/or orchestrate microservices backed by docker containers. Some of these accompanying tools are provided below for reference. 6 | 7 | * [Kubernetes](https://kubernetes.io/) -- Google's container orchestration and automation solution to schedule and maintain service state of docker containers. 8 | * [Mesos](https://mesosphere.com/why-mesos/?utm_source=adwords&utm_medium=g&utm_campaign=43843512431&utm_term=mesos&utm_content=196225818929&gclid=CjwKEAjwtJzLBRC7z43vr63nr3wSJABjJDgJ_9xn3RWHnkH_nDjxQs1X8U6YgQ0drZPoOTfLv9-4hhoCqN3w_wcB)/[Marathon](https://mesosphere.github.io/marathon/) -- Another Automation platform (Mesos) with an orchestration framework (Marathon) to ensure service state of docker containers. 9 | * [Rancher](http://rancher.com/) -- Opensource container management solution that makes it easy to deploy and manage containers in their own 'Cattle environments' and can even operate and manage other orchestration platforms like Kubernetes, Mesos/Marathon, and Docker Swarm. 10 | * [Docker Swarm](https://docs.docker.com/swarm/overview/) -- Docker's solution to automation and orchestration of clustered resources to provide a pool of Docker hosts into a single, virtual Docker host. 11 | * [Docker Compose](https://docs.docker.com/compose/) -- A automation tool for defining and running multi-container Docker applications. This tool is less sophisticated than the ones listed above and more simpler to use but with fewer features for larger deployments at scale. 12 | 13 | **In this module, we will be focusing on learning how to use Docker Compose to provision a self contained development environment based on a single input file that describes our desired state and configuration.** 14 | 15 | ## Docker Compose 16 | 17 | >Source of description comes from [Docker's documentation](https://docs.docker.com/compose/overview/). 18 | 19 | Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a Compose file to configure your application’s services. Then, using a single command, you create and start all the services from your configuration. 20 | 21 | Using Compose is basically a three-step process. 22 | 23 | **Step 1**: Define your container with a `Dockerfile` so it can be reproduced anywhere. Either have provide the [Dockerfile as an input](https://docs.docker.com/compose/reference/build/) or have the defined container hosted in a [docker registry](https://docs.docker.com/registry/) like [docker hub](hub.docker.com/). 24 | 25 | **Step 2**: Define the containers that make up your microservices in a `docker-compose.yml` file so it can be run together with other containers in an isolated environment. The `docker-compose.yml` basically consist of `key` : `value` pairs as per the [yaml syntax](https://github.com/Animosity/CraftIRC/wiki/Complete-idiot's-introduction-to-yaml) describing the desired state of your services. 26 | 27 | **Step 3:** Lastly, run the command `docker-compose up` and Compose will start and run your entire microservice based app as per the desired state. 28 | 29 | # Overview 30 | 31 | In this module, we are going to automate the deployment of a simple, self contained, dockerized [sandbox environment](https://github.com/Citrix-TechSpecialist/nitro-ide) to write scripts that issue [NITRO](http://docs.citrix.com/ja-jp/netscaler/11/nitro-api.html) commands to your NetScaler ADCs. In this case we will be issuing commands to a [NetScaler CPX](microloadbalancer.com) that will be locally provisioned on your machine to load balance simple containerized websites. However, it should be noted that this tutorial can be translated to develop and issue commands against other [NetScaler ADCs](https://www.citrix.com/products/netscaler-adc/platforms.html) as well if desired. 32 | 33 | The desired environment will have the following topology: 34 | 35 | ![Nitro Dev-box topology](./images/topology.jpg) 36 | 37 | **Services include:** 38 | 39 | * **Webserver A** which is a static containerized HTTP website 40 | 41 | * **Webserver B** which is another static containerized HTTP website 42 | 43 | * **NetScaler CPX** which will be the target NetScaler to send NITRO API calls to load balance webserver A and webserver B. 44 | 45 | * **Cloud9 IDE** which is web-based Interactive Developer Environment that allows for rapid scripting and coding through a web browser. 46 | 47 | >All the services above will be isolated in a dedicated [Docker Network](https://docs.docker.com/engine/userguide/networking/). Individual web interfaces that we will need direct external access to will have [external ports mapped](https://docs.docker.com/compose/compose-file/compose-file-v2/#ports) to the container for access from the underlay network (basically your host's LAN). 48 | 49 | ## Pre-requisite: Reset your environment before continuing. 50 | 51 | Reset the environment by typing in the following command in your sandbox environment: 52 | 53 | `sudo /dockerclean.sh` 54 | 55 | If you are following along on your local machine, enter the following commands to remove all docker containers, images, and docker volumes from your host: 56 | 57 | ``` 58 | docker kill $(docker ps -q) 59 | docker rm -f $(docker ps -a -q) 60 | docker rmi $(docker images -q) 61 | docker volume rm $(docker volume ls -qf) 62 | ``` 63 | 64 | ## Exercises 65 | 66 | Navigate to and complete the following exercises within Module 3: 67 | 68 | 1. [Create a docker-compose.yaml File](./Exercise-1) 69 | 2. [Compose an Environment](./Exercise-2) 70 | 71 | ### Shortcuts 72 | 73 | [Table of Contents](../) 74 | [Module 1](../Module-1) 75 | [Module 2](../Module-2) 76 | -------------------------------------------------------------------------------- /Module-3/images/topology.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Citrix-TechSpecialist/Docker-101/45bb1b6284bba2de2518b8fb6049997f668ff8b9/Module-3/images/topology.jpg -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Table of Contents 2 | 3 | 1. [Module 0-A: Install Docker Locally](https://hub.docker.com/?next=https%3A%2F%2Fhub.docker.com%2F) 4 | 2. [Module 0-B: Access your Docker Lab Development Box](./Module-0) 5 | 2. [Module 1: Running Docker Containers](./Module-1) 6 | 3. [Module 2: Creating Custom Images from Dockerfiles](./Module-2) 7 | 4. [Module 3: Using Docker Compose](./Module-3) 8 | 9 | # Introduction 10 | 11 | > Source of Introduction: comes from an article published in [InfoWold](http://www.infoworld.com/article/3204171/linux/what-is-docker-linux-containers-explained.html) 12 | 13 | [Docker containers](https://blog.docker.com/2016/05/docker-101-getting-to-know-docker/) are self-contained execution environments—with their own, isolated CPU, memory, block I/O, and network resources—that share the kernel of the host operating system. The result is **something that feels like a virtual machine, but sheds all the weight and startup overhead of a guest operating system.** 14 | 15 | To understand containers, we have to start with Linux [cgroups](https://sysadmincasts.com/episodes/14-introduction-to-linux-control-groups-cgroups) and [namespaces](http://blogs.igalia.com/dpino/2016/04/10/network-namespaces/), the Linux kernel features that create the walls between containers and other processes running on the host. Linux namespaces, originally developed by IBM, wrap a set of system resources and present them to a process to make it look like they are dedicated to that process. 16 | 17 | In short: 18 | 19 | * **Namespaces** :  Limits what the running process can see. I.E. processes can have their own view of the system’s resources. 20 | * **cgroups** :  Metering and limiting mechanism, they control how much of a system resource (CPU, memory) processes can use. 21 | 22 | In comparison to virtual machines, containers feel and act like independent operating system environments, but are actually layered on top of an existing OS similar to how a VM would be on top of a hypervisor. A visual below is provided for contextual aid in comparison with traditional VM architecture vs a docker containerized architecture. 23 | 24 | ![Container vs VMs](images/containers-visual.png) 25 | 26 | In this tutorial, we are going to gain hands on experience and learn the basics of docker containers, images, commands, and automate deployment of a simple self dockerized [sandbox environment](https://github.com/Citrix-TechSpecialist/nitro-ide/tree/0206630bd6903887d599613a42dd65da550cc37e) to develop scripts to issue [NITRO](http://docs.citrix.com/ja-jp/netscaler/11/nitro-api.html) commands to your NetScaler ADCs. 27 | 28 | # Pre-requisites 29 | 30 | * [Docker](https://docs.docker.com/engine/installation/) 31 | * You must install Docker in your local environment to do this tutorial. Follow the instructions in the link provided to install Docker on your operating system. 32 | * If you are following along in a ILT course, skip ahead to [Module 0-B: Access your Docker Lab Development Box](./Module-0) 33 | -------------------------------------------------------------------------------- /images/containers-visual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Citrix-TechSpecialist/Docker-101/45bb1b6284bba2de2518b8fb6049997f668ff8b9/images/containers-visual.png --------------------------------------------------------------------------------