Docker : Run a React app in a docker II (snapshot app with nginx)
Continued from Docker : Run a React app in a docker.
The only difference is that in this post, we'll use a difference React app based on Snap Shot - A photo gallery with search which was linked from https://reactjs.org/community/examples.html and it's using react-router-dom.
Let's start the project:
$ git clone https://github.com/Yog9/SnapShot.git $ cd SnapShot
npm (Node Package Manager) is a package manager for the JavaScript programming language. It has become the de facto package manager for the web. It is installed with Node.js
$ npm -v 6.14.5 $ node -v v11.9.0
Need to have node_modules, run the following command within the folder that has package.json file:
$ npm install
Start the app:
$ npm run-script start ... Compiled successfully! You can now view snapshot in the browser. Local: http://localhost:3000/SnapShot On Your Network: http://10.0.0.161:3000/SnapShot Note that the development build is not optimized. To create a production build, use yarn build.
Or this command to start the app:
$ npm start
$ docker --version Docker version 19.03.8, build afacb8b
Add the following Dockerfile to the root of the project:
# pull official base image FROM node:13.12.0-alpine # set working directory WORKDIR /app # add `/app/node_modules/.bin` to $PATH ENV PATH /app/node_modules/.bin:$PATH # install app dependencies COPY package.json ./ COPY package-lock.json ./ RUN npm install # add app COPY . ./ # start app CMD ["npm", "start"]
To speed up the creation of the Docker container, make sure to add a .dockerignore to our project to exclude such as node_modules from being sent to the Docker context. Here is the .dockerignore file:
node_modules npm-debug.log build .dockerignore **/.git **/.DS_Store **/node_modules
Build and tag the Docker image:
$ docker build -t snapshot:dev . Sending build context to Docker daemon 3.007MB Step 1/8 : FROM node:13.12.0-alpine ---> 483343d6c5f5 Step 2/8 : WORKDIR /app ---> Using cache ---> a4d081072ee9 Step 3/8 : ENV PATH /app/node_modules/.bin:$PATH ---> Using cache ---> 45ae875244e7 Step 4/8 : COPY package.json ./ ---> 90dd1a39759d Step 5/8 : COPY package-lock.json ./ ---> 7ceb616be776 Step 6/8 : RUN npm install ---> Running in 0a0ba989b4f7 ... Step 7/8 : COPY . ./ ---> 8ed5c2501fa0 Step 8/8 : CMD ["npm", "start"] ---> Running in a682a75513ab Removing intermediate container a682a75513ab ---> 9f7656e0372e Successfully built 9f7656e0372e Successfully tagged snapshot:dev $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE snapshot dev 9f7656e0372e 11 minutes ago 296MB
Let's create and run a new container instance from the image we created in the previous section.
$ docker run -it --rm \ -v ${PWD}:/app \ -v /app/node_modules \ -p 3001:3000 \ -e CHOKIDAR_USEPOLLING=true \ snapshot:dev ... Compiled successfully! You can now view snapshot in the browser. Local: http://localhost:3000/SnapShot On Your Network: http://172.17.0.2:3000/SnapShot Note that the development build is not optimized. To create a production build, use yarn build.
-it
starts the container in interactive mode.--rm
removes the container and volumes after the container exits.-v ${PWD}:/app
mounts the code into the container at "/app".Source: https://docs.docker.com/storage/
-v /app/node_modules
Since we want to use the container version of the "node_modules" folder, we configured another volume: -v /app/node_modules. We should now be able to remove the local "node_modules" flavor.
-p 3001:3000
exposes port 3000 to other Docker containers on the same network (for inter-container communication) and port 3001 to the host.-e CHOKIDAR_USEPOLLING=true
enables a polling mechanism via chokidar (which wraps fs.watch, fs.watchFile, and fsevents) so that hot-reloading will work. (check npm:chokidar
Open a browser to http://localhost:3001/ and we should see the app:
Stop the container, ^C.
We can check the files in the container:
$ docker run -itd --rm \ -v ${PWD}:/app \ -v /app/node_modules \ -p 3001:3000 \ -e CHOKIDAR_USEPOLLING=true \ snapshot:dev 57d91b100c84a721368614468cb362d37d9592e7c3497e6f203eaf32f4fbe6ac $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 57d91b100c84 snapshot:dev "docker-entrypoint.s…" 28 seconds ago Up 20 seconds 0.0.0.0:3001->3000/tcp brave_chebyshev $ docker exec -it 57d91b100c84 sh /app #
Stop the container:
$ docker stop 57d91b100c84
Add the following docker-compose.yaml file to the project root:
version: '3.7' services: snapshot: container_name: snapshot build: context: . dockerfile: Dockerfile volumes: - '.:/app' - '/app/node_modules' ports: - 3001:3000 environment: - CHOKIDAR_USEPOLLING=true
Note that we're using the anonymous volume ('/app/node_modules') so the node_modules directory would not be overwritten by the mounting of the host directory at runtime. Actually, this would happen:
- build - The node_modules directory is created in the image.
- run - The current directory is mounted into the container, overwriting the node_modules that were installed during the build.
Let's build the image and spin up the container:
$ docker-compose up -d --build Creating network "snapshot_default" with the default driver Building snapshot Step 1/8 : FROM node:13.12.0-alpine ---> 483343d6c5f5 Step 2/8 : WORKDIR /app ---> Using cache ---> a4d081072ee9 Step 3/8 : ENV PATH /app/node_modules/.bin:$PATH ---> Using cache ---> 45ae875244e7 Step 4/8 : COPY package.json ./ ---> 0a93457433ca Step 5/8 : COPY package-lock.json ./ ---> 0d5d80470dc9 Step 6/8 : RUN npm install ---> Running in 66cf147b6cdf ... Step 7/8 : COPY . ./ ---> 0e54ed1843e9 Step 8/8 : CMD ["npm", "start"] ---> Running in 314fe141d8f4 Removing intermediate container 314fe141d8f4 ---> 8561671e2615 Successfully built 8561671e2615 Successfully tagged snapshot_snapshot:latest Creating snapshot ... done $ docker-compose ps Name Command State Ports ------------------------------------------------------------- snapshot docker-entrypoint.sh npm start Exit 0
Note that the container exited with 0. To resolve the issue, we need to add stdin_open: true
to the docker-compose file as shown below:
version: '3.7' services: snapshot: container_name: snapshot build: context: . dockerfile: Dockerfile volumes: - '.:/app' - '/app/node_modules' ports: - 3001:3000 environment: - CHOKIDAR_USEPOLLING=true stdin_open: true
We can try to up the container again:
$ docker-compose up -d --build Building snapshot Step 1/8 : FROM node:13.12.0-alpine ---> 483343d6c5f5 Step 2/8 : WORKDIR /app ---> Using cache ---> a4d081072ee9 Step 3/8 : ENV PATH /app/node_modules/.bin:$PATH ---> Using cache ---> 45ae875244e7 Step 4/8 : COPY package.json ./ ---> Using cache ---> 0a93457433ca Step 5/8 : COPY package-lock.json ./ ---> Using cache ---> 0d5d80470dc9 Step 6/8 : RUN npm install ---> Using cache ---> 8f0adff89f08 Step 7/8 : COPY . ./ ---> 8454437949ba Step 8/8 : CMD ["npm", "start"] ---> Running in 90f8075c9f95 Removing intermediate container 90f8075c9f95 ---> 8bfa998ad98f Successfully built 8bfa998ad98f Successfully tagged snapshot_snapshot:latest Recreating snapshot ... done $ docker-compose ps Name Command State Ports -------------------------------------------------------------------------- snapshot docker-entrypoint.sh npm start Up 0.0.0.0:3001->3000/tcp
We may want to make sure the app is running in the browser and test hot-reloading again.
Bring down the container before moving on:
$ docker-compose stop Stopping snapshot ... done
So far, we've been working on React app for development environment. Now, let's make it on production environment.
We'll be using nginx to serve the content of our React application.
We take advantage of the multistage build pattern: "One of the most challenging things about building images is keeping the image size down. Each instruction in the Dockerfile adds a layer to the image, and you need to remember to clean up any artifacts you don’t need before moving on to the next layer."
With multi-stage builds, we use multiple FROM
statements in our Dockerfile.
Each FROM instruction can use a different base, and each of them begins a new stage of the build.
We can selectively copy artifacts from one stage to another,
leaving behind everything we don't want in the final image.
This post ( Create lean Docker images using the Builder Pattern) shows it with an example.
So, we create a temporary image used for building the artifact – the production-ready React static files – that is then copied over to the production image. The temporary build image is discarded along with the original files and folders associated with the image. This produces a lean, production-ready image.
Here is our Dockerfile.prod file:
# build environment FROM node:13.12.0-alpine as builder WORKDIR /app ENV PATH /app/node_modules/.bin:$PATH COPY package.json ./ COPY package-lock.json ./ RUN npm ci --silent RUN npm install react-scripts@3.4.1 -g --silent COPY . ./ RUN npm run build # production environment FROM nginx:stable-alpine COPY --from=builder /app/build /usr/share/nginx/html EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
By default, the stages are not named. However, as we can see from the Dockerfile.prod file above, we name our stage, by adding an as <NAME> to the FROM instruction, in our case, it is builder.
And then using the name in the COPY instruction. This means that even if the instructions in our Dockerfile are re-ordered later, the COPY doesn't break.
Note that we have another way - we can run npm run-script build
to create ./build/
and then just copy the ./build/ folder to container as done in
Docker : Run a React app in a minikube.
But there is one thing we need to handle is the PUBLIC_URL. React's npm run build
will replace the PUBLIC_URL with the name of the root folder.
So, we may get 404 error, for instance, from index.html which is similar to this:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" /> <link href="https://fonts.googleapis.com/css?family=Josefin+Sans|Lobster" rel="stylesheet" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <meta name="theme-color" content="#000000" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <title>Snap Shot</title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> <span class="forkongithub"><a href="https://github.com/Yog9/SnapShot">Fork me on GitHub</a></span> </body> </html>
Therefore, we need to set PUBLIC_URL it as "/" in .env within the project root folder where package.json is.
Using the production Dockerfile, build and tag the Docker image:
$ docker build -f Dockerfile.prod -t snapshot:prod . Sending build context to Docker daemon 3.009MB Step 1/13 : FROM node:13.12.0-alpine as builder ---> 483343d6c5f5 Step 2/13 : WORKDIR /app ---> Using cache ---> a4d081072ee9 Step 3/13 : ENV PATH /app/node_modules/.bin:$PATH ---> Using cache ---> 45ae875244e7 Step 4/13 : COPY package.json ./ ---> cdc5af7bb81a Step 5/13 : COPY package-lock.json ./ ---> 08fb0cbc091f Step 6/13 : RUN npm ci --silent ---> Running in f26d26d87ac3 ... Step 7/13 : RUN npm install react-scripts@3.4.1 -g --silent ---> Running in 0c51eb918600 /usr/local/bin/react-scripts -> /usr/local/lib/node_modules/react-scripts/bin/react-scripts.js ... Step 8/13 : COPY . ./ ---> 23430759a38b Step 9/13 : RUN npm run build ---> Running in d89d86a5cb43 ... Step 10/13 : FROM nginx:stable-alpine ---> ab94f84cc474 Step 11/13 : COPY --from=builder /app/build /usr/share/nginx/html ---> bb79a41900c2 Step 12/13 : EXPOSE 80 ---> Running in 73b51b978d64 Removing intermediate container 73b51b978d64 ---> 3bb0fd2d1cbe Step 13/13 : CMD ["nginx", "-g", "daemon off;"] ---> Running in 24bdaca1f05b Removing intermediate container 24bdaca1f05b ---> 222d8e465ff6 Successfully built 222d8e465ff6 Successfully tagged snapshot:prod
Spin up the container:
$ docker run -it --rm -p 8787:80 snapshot:prod $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c931c9861125 snapshot:prod "nginx -g 'daemon of…" 33 seconds ago Up 32 seconds 0.0.0.0:8787->80/tcp agitated_keldysh
Navigate to http://localhost:8787/ in a browser to view the app:
Here is the sample responses for requests:
172.17.0.1 - - [30/May/2020:21:59:56 +0000] "GET / HTTP/1.1" 200 2307 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36" "-" 172.17.0.1 - - [30/May/2020:21:59:56 +0000] "GET /static/js/2.bec71660.chunk.js HTTP/1.1" 200 174732 "http://localhost:8787/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36" "-" 172.17.0.1 - - [30/May/2020:21:59:56 +0000] "GET /static/css/main.d03163bd.chunk.css HTTP/1.1" 200 2942 "http://localhost:8787/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36" "-" 172.17.0.1 - - [30/May/2020:21:59:56 +0000] "GET /static/js/main.0184f16a.chunk.js HTTP/1.1" 200 5384 "http://localhost:8787/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36" "-" 172.17.0.1 - - [30/May/2020:21:59:57 +0000] "GET /static/css/main.d03163bd.chunk.css.map HTTP/1.1" 200 5614 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36" "-" 172.17.0.1 - - [30/May/2020:21:59:57 +0000] "GET /static/js/2.bec71660.chunk.js.map HTTP/1.1" 200 524039 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36" "-" 172.17.0.1 - - [30/May/2020:21:59:57 +0000] "GET /static/js/main.0184f16a.chunk.js.map HTTP/1.1" 200 17755 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36" "-" 172.17.0.1 - - [30/May/2020:21:59:58 +0000] "GET /manifest.json HTTP/1.1" 200 492 "http://localhost:8787/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36" "-" 172.17.0.1 - - [30/May/2020:21:59:58 +0000] "GET /logo192.png HTTP/1.1" 200 5347 "http://localhost:8787/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36" "-" ...
Now, let's try a new Docker Compose file (docker-compose.prod.yaml):
version: '3.7' services: snapshot: container_name: snapshot build: context: . dockerfile: Dockerfile.prod ports: - '8877:80'
Spin up the container:
$ docker-compose -f docker-compose.prod.yaml up -d --build Building snapshot Step 1/13 : FROM node:13.12.0-alpine as builder ---> 483343d6c5f5 Step 2/13 : WORKDIR /app ---> Using cache ---> 596eea795a82 Step 3/13 : ENV PATH /app/node_modules/.bin:$PATH ---> Using cache ---> f212aa44c486 Step 4/13 : COPY package.json ./ ---> 4bf8e17b97c1 Step 5/13 : COPY package-lock.json ./ ---> 7d1b0ba25810 Step 6/13 : RUN npm ci --silent ---> Running in 8f0b20b21eec Skipping 'fsevents' build as platform linux is not supported Skipping 'fsevents' build as platform linux is not supported Skipping 'fsevents' build as platform linux is not supported added 1680 packages in 56.261s Removing intermediate container 8f0b20b21eec ---> 73b253a57155 Step 7/13 : RUN npm install react-scripts@3.4.1 -g --silent ---> Running in 623417a8289f /usr/local/bin/react-scripts -> /usr/local/lib/node_modules/react-scripts/bin/react-scripts.js + react-scripts@3.4.1 added 1622 packages from 751 contributors in 98.856s Removing intermediate container 623417a8289f ---> 1607caed7d1e Step 8/13 : COPY . ./ ---> 5d0099ea678f Step 9/13 : RUN npm run build ---> Running in b7c3f1e4341b > snapshot@0.1.0 build /app > react-scripts build Creating an optimized production build... Compiled successfully. File sizes after gzip: 53.21 KB build/static/js/2.bec71660.chunk.js 1.92 KB build/static/js/main.0184f16a.chunk.js 1.11 KB build/static/css/main.d03163bd.chunk.css 771 B build/static/js/runtime-main.20780a71.js The project was built assuming it is hosted at /. You can control this with the homepage field in your package.json. The build folder is ready to be deployed. You may serve it with a static server: yarn global add serve serve -s build Find out more about deployment here: bit.ly/CRA-deploy Removing intermediate container b7c3f1e4341b ---> 49c6b301ed34 Step 10/13 : FROM nginx:stable-alpine ---> ab94f84cc474 Step 11/13 : COPY --from=builder /app/build /usr/share/nginx/html ---> Using cache ---> ad70ca3daea4 Step 12/13 : EXPOSE 80 ---> Using cache ---> 651986658c0f Step 13/13 : CMD ["nginx", "-g", "daemon off;"] ---> Using cache ---> ae97ccebfb66 Successfully built ae97ccebfb66 Successfully tagged snapshot_snapshot:latest Creating snapshot ... done $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2d84af0dee5e snapshot_snapshot "nginx -g 'daemon of…" About a minute ago Up About a minute 0.0.0.0:8877->80/tcp snapshot
Test it out again on port 8877:
We can go into the container and check the processes:
$ docker exec -it 2d84af0dee5e sh / # ps aux PID USER TIME COMMAND 1 root 0:00 nginx: master process nginx -g daemon off; 6 nginx 0:00 nginx: worker process 7 nginx 0:00 nginx: worker process 8 root 0:00 sh 14 root 0:00 ps aux / #
- Hello World
- React Hello World: Your First React App (2019)
- Dockerizing a React App
- Create lean Docker images using the Builder Pattern
- React in Docker with Nginx, built with multi-stage Docker builds, including testing
Docker & K8s
- Docker install on Amazon Linux AMI
- Docker install on EC2 Ubuntu 14.04
- Docker container vs Virtual Machine
- Docker install on Ubuntu 14.04
- Docker Hello World Application
- Nginx image - share/copy files, Dockerfile
- Working with Docker images : brief introduction
- Docker image and container via docker commands (search, pull, run, ps, restart, attach, and rm)
- More on docker run command (docker run -it, docker run --rm, etc.)
- Docker Networks - Bridge Driver Network
- Docker Persistent Storage
- File sharing between host and container (docker run -d -p -v)
- Linking containers and volume for datastore
- Dockerfile - Build Docker images automatically I - FROM, MAINTAINER, and build context
- Dockerfile - Build Docker images automatically II - revisiting FROM, MAINTAINER, build context, and caching
- Dockerfile - Build Docker images automatically III - RUN
- Dockerfile - Build Docker images automatically IV - CMD
- Dockerfile - Build Docker images automatically V - WORKDIR, ENV, ADD, and ENTRYPOINT
- Docker - Apache Tomcat
- Docker - NodeJS
- Docker - NodeJS with hostname
- Docker Compose - NodeJS with MongoDB
- Docker - Prometheus and Grafana with Docker-compose
- Docker - StatsD/Graphite/Grafana
- Docker - Deploying a Java EE JBoss/WildFly Application on AWS Elastic Beanstalk Using Docker Containers
- Docker : NodeJS with GCP Kubernetes Engine
- Docker : Jenkins Multibranch Pipeline with Jenkinsfile and Github
- Docker : Jenkins Master and Slave
- Docker - ELK : ElasticSearch, Logstash, and Kibana
- Docker - ELK 7.6 : Elasticsearch on Centos 7
- Docker - ELK 7.6 : Filebeat on Centos 7
- Docker - ELK 7.6 : Logstash on Centos 7
- Docker - ELK 7.6 : Kibana on Centos 7
- Docker - ELK 7.6 : Elastic Stack with Docker Compose
- Docker - Deploy Elastic Cloud on Kubernetes (ECK) via Elasticsearch operator on minikube
- Docker - Deploy Elastic Stack via Helm on minikube
- Docker Compose - A gentle introduction with WordPress
- Docker Compose - MySQL
- MEAN Stack app on Docker containers : micro services
- MEAN Stack app on Docker containers : micro services via docker-compose
- Docker Compose - Hashicorp's Vault and Consul Part A (install vault, unsealing, static secrets, and policies)
- Docker Compose - Hashicorp's Vault and Consul Part B (EaaS, dynamic secrets, leases, and revocation)
- Docker Compose - Hashicorp's Vault and Consul Part C (Consul)
- Docker Compose with two containers - Flask REST API service container and an Apache server container
- Docker compose : Nginx reverse proxy with multiple containers
- Docker & Kubernetes : Envoy - Getting started
- Docker & Kubernetes : Envoy - Front Proxy
- Docker & Kubernetes : Ambassador - Envoy API Gateway on Kubernetes
- Docker Packer
- Docker Cheat Sheet
- Docker Q & A #1
- Kubernetes Q & A - Part I
- Kubernetes Q & A - Part II
- Docker - Run a React app in a docker
- Docker - Run a React app in a docker II (snapshot app with nginx)
- Docker - NodeJS and MySQL app with React in a docker
- Docker - Step by Step NodeJS and MySQL app with React - I
- Installing LAMP via puppet on Docker
- Docker install via Puppet
- Nginx Docker install via Ansible
- Apache Hadoop CDH 5.8 Install with QuickStarts Docker
- Docker - Deploying Flask app to ECS
- Docker Compose - Deploying WordPress to AWS
- Docker - WordPress Deploy to ECS with Docker-Compose (ECS-CLI EC2 type)
- Docker - WordPress Deploy to ECS with Docker-Compose (ECS-CLI Fargate type)
- Docker - ECS Fargate
- Docker - AWS ECS service discovery with Flask and Redis
- Docker & Kubernetes : minikube
- Docker & Kubernetes 2 : minikube Django with Postgres - persistent volume
- Docker & Kubernetes 3 : minikube Django with Redis and Celery
- Docker & Kubernetes 4 : Django with RDS via AWS Kops
- Docker & Kubernetes : Kops on AWS
- Docker & Kubernetes : Ingress controller on AWS with Kops
- Docker & Kubernetes : HashiCorp's Vault and Consul on minikube
- Docker & Kubernetes : HashiCorp's Vault and Consul - Auto-unseal using Transit Secrets Engine
- Docker & Kubernetes : Persistent Volumes & Persistent Volumes Claims - hostPath and annotations
- Docker & Kubernetes : Persistent Volumes - Dynamic volume provisioning
- Docker & Kubernetes : DaemonSet
- Docker & Kubernetes : Secrets
- Docker & Kubernetes : kubectl command
- Docker & Kubernetes : Assign a Kubernetes Pod to a particular node in a Kubernetes cluster
- Docker & Kubernetes : Configure a Pod to Use a ConfigMap
- AWS : EKS (Elastic Container Service for Kubernetes)
- Docker & Kubernetes : Run a React app in a minikube
- Docker & Kubernetes : Minikube install on AWS EC2
- Docker & Kubernetes : Cassandra with a StatefulSet
- Docker & Kubernetes : Terraform and AWS EKS
- Docker & Kubernetes : Pods and Service definitions
- Docker & Kubernetes : Service IP and the Service Type
- Docker & Kubernetes : Kubernetes DNS with Pods and Services
- Docker & Kubernetes : Headless service and discovering pods
- Docker & Kubernetes : Scaling and Updating application
- Docker & Kubernetes : Horizontal pod autoscaler on minikubes
- Docker & Kubernetes : From a monolithic app to micro services on GCP Kubernetes
- Docker & Kubernetes : Rolling updates
- Docker & Kubernetes : Deployments to GKE (Rolling update, Canary and Blue-green deployments)
- Docker & Kubernetes : Slack Chat Bot with NodeJS on GCP Kubernetes
- Docker & Kubernetes : Continuous Delivery with Jenkins Multibranch Pipeline for Dev, Canary, and Production Environments on GCP Kubernetes
- Docker & Kubernetes : NodePort vs LoadBalancer vs Ingress
- Docker & Kubernetes : MongoDB / MongoExpress on Minikube
- Docker & Kubernetes : Load Testing with Locust on GCP Kubernetes
- Docker & Kubernetes : MongoDB with StatefulSets on GCP Kubernetes Engine
- Docker & Kubernetes : Nginx Ingress Controller on Minikube
- Docker & Kubernetes : Setting up Ingress with NGINX Controller on Minikube (Mac)
- Docker & Kubernetes : Nginx Ingress Controller for Dashboard service on Minikube
- Docker & Kubernetes : Nginx Ingress Controller on GCP Kubernetes
- Docker & Kubernetes : Kubernetes Ingress with AWS ALB Ingress Controller in EKS
- Docker & Kubernetes : Setting up a private cluster on GCP Kubernetes
- Docker & Kubernetes : Kubernetes Namespaces (default, kube-public, kube-system) and switching namespaces (kubens)
- Docker & Kubernetes : StatefulSets on minikube
- Docker & Kubernetes : RBAC
- Docker & Kubernetes Service Account, RBAC, and IAM
- Docker & Kubernetes - Kubernetes Service Account, RBAC, IAM with EKS ALB, Part 1
- Docker & Kubernetes : Helm Chart
- Docker & Kubernetes : My first Helm deploy
- Docker & Kubernetes : Readiness and Liveness Probes
- Docker & Kubernetes : Helm chart repository with Github pages
- Docker & Kubernetes : Deploying WordPress and MariaDB with Ingress to Minikube using Helm Chart
- Docker & Kubernetes : Deploying WordPress and MariaDB to AWS using Helm 2 Chart
- Docker & Kubernetes : Deploying WordPress and MariaDB to AWS using Helm 3 Chart
- Docker & Kubernetes : Helm Chart for Node/Express and MySQL with Ingress
- Docker & Kubernetes : Deploy Prometheus and Grafana using Helm and Prometheus Operator - Monitoring Kubernetes node resources out of the box
- Docker & Kubernetes : Deploy Prometheus and Grafana using kube-prometheus-stack Helm Chart
- Docker & Kubernetes : Istio (service mesh) sidecar proxy on GCP Kubernetes
- Docker & Kubernetes : Istio on EKS
- Docker & Kubernetes : Istio on Minikube with AWS EC2 for Bookinfo Application
- Docker & Kubernetes : Deploying .NET Core app to Kubernetes Engine and configuring its traffic managed by Istio (Part I)
- Docker & Kubernetes : Deploying .NET Core app to Kubernetes Engine and configuring its traffic managed by Istio (Part II - Prometheus, Grafana, pin a service, split traffic, and inject faults)
- Docker & Kubernetes : Helm Package Manager with MySQL on GCP Kubernetes Engine
- Docker & Kubernetes : Deploying Memcached on Kubernetes Engine
- Docker & Kubernetes : EKS Control Plane (API server) Metrics with Prometheus
- Docker & Kubernetes : Spinnaker on EKS with Halyard
- Docker & Kubernetes : Continuous Delivery Pipelines with Spinnaker and Kubernetes Engine
- Docker & Kubernetes : Multi-node Local Kubernetes cluster : Kubeadm-dind (docker-in-docker)
- Docker & Kubernetes : Multi-node Local Kubernetes cluster : Kubeadm-kind (k8s-in-docker)
- Docker & Kubernetes : nodeSelector, nodeAffinity, taints/tolerations, pod affinity and anti-affinity - Assigning Pods to Nodes
- Docker & Kubernetes : Jenkins-X on EKS
- Docker & Kubernetes : ArgoCD App of Apps with Heml on Kubernetes
- Docker & Kubernetes : ArgoCD on Kubernetes cluster
- Docker & Kubernetes : GitOps with ArgoCD for Continuous Delivery to Kubernetes clusters (minikube) - guestbook
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization