Cloud Web App Architectures - Part 2

Continuing on from part 1 where we introduced the app’s architecture and implemented the solution using Docker and Docker Compose, in this article we will address the issue of distributing the images. Once the Docker image is created it becomes an artifact, two of the biggest advantages that containers add to the software development lifecycle (SDLC) is the fact that they are self-sufficient and immutable.

Being immutable doesn’t lend them to be used with software source control management (SCM) systems like Git or Mercurial, so we need a different type of solution to distribute these Docker images - a container registry.

Container Registry

As the diagram above illustrates, there is a build stage that happens in a build server, this is where the docker build .. command is executed. The resulting image is an artifact that is immutable, and can be signed with a security certificate for an added layer of security. This image can then be uploaded to a container registry in a process called push, using a command like docker push .... Even though these repositories are called container registries, what is actually “pushed” is a Docker image not a container.

Once the image is on the container registiries it can then be downloaded into multiple targets where containers of the image need to be created, this processes is called pull, executed through a command like docker pull .... There are several solutions for container registries ranging from hosted solutions to on premise products.

On top of providing a centralised distribution centre for Docker images, some of these products provide quite useful features, one of them being Role Based Access Control (RBAC). RBAC adds a layer of security which allows teams to have granular control of who has access to do what with the images. For example if the developers need to be able to both push and pull images from the registry while Quality Assurance (QA) team mebers only need to be able to pull images but not push, RBAC allows teams to define this.

Hosted providers of container registries as a service include all the major cloud prviders;

There are also self-hosted options for teams that require that level of control, examples include;

In this article we are going to use Gitlab’s hosted service which is an excellent product that is available at no charge. So we want to be in a position where all our images (the ones we built and their dependencies) are fetched from repositories, separating the build stage from the run stage. Our docker-compose.yml file (that we put together in the previous article) should not have any build steps, it should look like this instead;

version: '3'
services:
  reverse-proxy:
    image: "jwilder/nginx-proxy"
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
    depends_on:
      - nilipie-web
      - nilipie-api
  nilipie-web:
    # build: "./nilipie-web/"
    image: "registry.gitlab.com/haddad/nilipie:1.0.0"
    container_name: "nilipie-web"
    environment:        
      - VIRTUAL_HOST=www.nilipie.com 
    depends_on:
      - nilipie-api
  nilipie-api:
    # build: "./nilipie-api/"
    image: "registry.gitlab.com/haddad/nilipie-api:1.0.0"
    container_name: "nilipie-api"
    links:
    - mongodb:mongodb
    environment:
      - MONGODB_URL=mongodb://mongodb:27017/nilipie
      - VIRTUAL_HOST=api.nilipie.com
    depends_on:
      - mongodb
  mongodb:
    image: "mongo:latest"
    container_name: "mongodb"

The Build Stage

This is the stage that the source code is retrieved from the SCM server, most likely tagged as a release, and the release is packaged into a self sufficient immutable build in the form of a Docker image. This image is then pushed to the Container Registry ready to be ran in relevant upstream environments.

So first we are going to make sure we don’t have the two images we built earlier in our local images repository;

docker images
REPOSITORY                                 TAG                 IMAGE ID            CREATED             SIZE
git_nilipie-web                            latest              62c69deb61d6        4 days ago          186MB
git_nilipie-api                            latest              715b4163393c        4 days ago          109MB
mongo                                      latest              052ca8f03af8        12 days ago         381MB
...

We remove both of them

λ docker rmi --force git_nilipie-web git_nilipie-api                                                              
Untagged: git_nilipie-web:latest                                                                                  
Deleted: sha256:62c69deb61d66f7b1101634f4f2c9ded36bde6276cc7c41239df37b08a8cb750                                  
Deleted: sha256:d9ca6919ef22bdac106561fcb8d5a51ecb03f1790f31b34645c2057d4ddc291f                                  
Deleted: sha256:ce54529321735aef880ee4538efdaed6b753cbe1451dd4e06b2803d7a6d4c1fb                                  
Untagged: git_nilipie-api:latest                                                                                  
Deleted: sha256:715b4163393cff4da3f415a08f6531a381036a69ad4406c4be22c77667b7b10b                                  
Deleted: sha256:69fbda258f68427f42282add1441d2975dc091840b4f2fcbb590fe8ea71d8130                                  
Deleted: sha256:301f06f88fc0658f8ccda7c5d8fd1b05372a951140ec3bcb5e6953399afead31                                  
Deleted: sha256:6887de9181acf3ad32d6e3a7a26b55de32837fb5f2e18301079fb703b60d3429                                  
Deleted: sha256:a0ea9ddfb9704119dbb3390237f3af3d6ff82a3f6ac80db9baf01ac75531320d                                  
                                                                                                                  
                                                                                              
docker images                                                                                                   
REPOSITORY                                 TAG                 IMAGE ID            CREATED             SIZE       
mongo                                      latest              052ca8f03af8        12 days ago         381MB      
...

Next we are going to rebuild our images but push them to a remote container registry in Gitlab instead. First step is to log in to the repository

docker login registry.gitlab.com
Username: [email protected]
Password:
Login Succeeded

Then build the images with the proper tags

docker build -t registry.gitlab.com/haddad/nilipie-api:1.0.0 .
Sending build context to Docker daemon  13.81MB
Step 1/6 : FROM node:alpine
 ---> 5ffbcf1d9932
Step 2/6 : WORKDIR /app
 ---> Running in a1037f7b952f
Removing intermediate container a1037f7b952f
 ---> d4b528406854
Step 3/6 : ADD . /app
 ---> 4ada6cf9bd97
Step 4/6 : RUN yarn install
 ---> Running in f1198ac8e2ea
yarn install v1.9.4
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
Done in 5.66s.
Removing intermediate container f1198ac8e2ea
 ---> bff952bb93fe
Step 5/6 : EXPOSE 80
 ---> Running in f1134c7b04d9
Removing intermediate container f1134c7b04d9
 ---> a830e7f75e8b
Step 6/6 : CMD ["yarn", "start"]
 ---> Running in d941050c9a61
Removing intermediate container d941050c9a61
 ---> 548bc9cc30f6
Successfully built 548bc9cc30f6
Successfully tagged registry.gitlab.com/haddad/nilipie-api:1.0.0
docker build -t registry.gitlab.com/haddad/nilipie:1.0.0 .      
Sending build context to Docker daemon  3.736MB                   
Step 1/4 : FROM kyma/docker-nginx                                 
 ---> eb883be3763d                                                
Step 2/4 : ADD . /var/www                                         
 ---> e03493620f07                                                
Step 3/4 : EXPOSE 80                                              
 ---> Running in 06516b329d2c                                     
Removing intermediate container 06516b329d2c                      
 ---> 5201b172622f                                                
Step 4/4 : CMD 'nginx'                                            
 ---> Running in 6f6cd113b5c1                                     
Removing intermediate container 6f6cd113b5c1                      
 ---> 5db32e960cd8                                                
Successfully built 5db32e960cd8                                   
Successfully tagged registry.gitlab.com/haddad/nilipie:1.0.0

Now we will have these two images in our local image repository

docker images
REPOSITORY                                 TAG                 IMAGE ID            CREATED              SIZE
registry.gitlab.com/haddad/nilipie         1.0.0               5db32e960cd8        About a minute ago   186MB
registry.gitlab.com/haddad/nilipie-api     1.0.0               548bc9cc30f6        2 minutes ago        109MB
mongo                                      latest              052ca8f03af8        12 days ago          381MB
...

We now push them both to the remote container registry at Gitlab

docker push registry.gitlab.com/haddad/nilipie
The push refers to repository [registry.gitlab.com/haddad/nilipie]
327c4e3f6689: Pushed
368a7dffbe7b: Pushed
62ebc37d2e70: Pushed
b773e9979a9b: Pushed
4204ee0217af: Pushed
746fbc761b74: Pushed
f30c2bee94eb: Pushed
e71f17e9dd2b: Pushed
1d64af5e5b60: Pushed
42ed799f0d4c: Pushed
5f70bf18a086: Pushed
b91b7f6cd891: Pushed
7ec8eb54c64e: Pushed
6eb35183d3b8: Pushed
1.0.0: digest: sha256:e5f0ab9ed6f9f25720e1bdb05284f8980939db440441602e67ea6b4eb2724ee6 size: 4056

λ docker push registry.gitlab.com/haddad/nilipie-api
The push refers to repository [registry.gitlab.com/haddad/nilipie-api]
227d8a9131c0: Pushed
6bfbbc25b203: Pushed
4cd17b4c6c40: Pushed
6fbd66047f80: Pushed
d29e6a09f93f: Pushed
df64d3292fd6: Pushed
1.0.0: digest: sha256:308a8b94b9f9bfccf01dff881685e318d7054a1690fca22748c00bf0d7fda2e4 size: 1579

We can also verify that these images are now in the container registry from Gitlab’s wed front end;

We now have completed the build stage and the images are available in the remote container registry.

The Run Stage

In the run stage the previously created images which are now available in a container registry are pulled down and ran. This can be done in many environments, developer’s laptops or desktop machines, QA environment (whether it may be their individual machines or a virtualized server cluster) or ops running the containers in a production environment. All these will be retrieved from the same image in the container registry - build once run many times.

To elaborate the point of fetching the images remotely we will first remove again the two images from our local image repository so that Docker pulls all images from remote registries;

docker images
REPOSITORY                                 TAG                 IMAGE ID            CREATED             SIZE
registry.gitlab.com/haddad/nilipie         1.0.0               5db32e960cd8        16 minutes ago      186MB
registry.gitlab.com/haddad/nilipie-api     1.0.0               548bc9cc30f6        17 minutes ago      109MB
mongo                                      latest              052ca8f03af8        12 days ago         381MB
...
docker rmi 5db32e960cd8 548bc9cc30f6
Untagged: registry.gitlab.com/haddad/nilipie:1.0.0
Untagged: registry.gitlab.com/haddad/nilipie@sha256:e5f0ab9ed6f9f25720e1bdb05284f8980939db440441602e67ea6b4eb2724ee6
Deleted: sha256:5db32e960cd86746b8fecc82b0d3bbb476cb6c802b8d46b553c4a1fafdccb59d
Deleted: sha256:5201b172622f0cd831b518e387ce3ec8b104282e7f79e014ad861ea8f4f65d40
Deleted: sha256:e03493620f070c0dc3f9ddacdd5cfa2b481559ad1bc6a703d7f38055d9d07db7
Deleted: sha256:c618c66bf0ea0d339e010b4349804aac9d09987a4f76a95aa3783552787dbbcb
Untagged: registry.gitlab.com/haddad/nilipie-api:1.0.0
Untagged: registry.gitlab.com/haddad/nilipie-api@sha256:308a8b94b9f9bfccf01dff881685e318d7054a1690fca22748c00bf0d7fda2e4
Deleted: sha256:548bc9cc30f6368205e257121cd8f81d4394bb92bebb2ad7934eafc8508889f4
Deleted: sha256:a830e7f75e8b1d114e0eda324eb1c6eaf0841a97b399a403f56164136ead5e87
Deleted: sha256:bff952bb93fededd15379e3ba3b4f8413719ef89073ad58d02468150d59305c7
Deleted: sha256:4bdecc3e4e9e776229a895e54d3454ecc31d5426f3b676a3486a8110912d967c
Deleted: sha256:4ada6cf9bd97b32ba5be9c9962f39022af921faa9ac196d2e67976a68da5ec46
Deleted: sha256:44949d138112716dcb0b8aee9f265fafce6be617eed058ea96c637061937de95
Deleted: sha256:d4b528406854aefda26b2cf0dc77bae2b482ed96db878d6c5393054485a0a8cb
Deleted: sha256:0326eeae1a6f21c73fdce720a744a0dd77b5fe43459e5ea40bc1892c7d646a04

λ docker images
REPOSITORY                                 TAG                 IMAGE ID            CREATED             SIZE
mongo                                      latest              052ca8f03af8        12 days ago         381MB
...

Now we use Docker Compose to launch our service

λ docker-compose up                                                                                                                      
Pulling mongodb (mongo:latest)...                                                                                                        
latest: Pulling from library/mongo                                                                                                       
3b37166ec614: Pull complete                                                                                                              
504facff238f: Pull complete                                                                                                              
ebbcacd28e10: Pull complete                                                                                                              
c7fb3351ecad: Pull complete                                                                                                              
2e3debadcbf7: Pull complete                                                                                                              
004c7a04feb1: Pull complete                                                                                                              
897284d7f640: Pull complete                                                                                                              
af4d2dae1422: Pull complete                                                                                                              
5e988d91970a: Pull complete                                                                                                              
aebe46e3fb07: Pull complete                                                                                                              
6e52ad506433: Pull complete                                                                                                              
47d2bdbad490: Pull complete                                                                                                              
0b15ac2388a7: Pull complete                                                                                                              
7b8821d8bba9: Pull complete                                                                                                              
Digest: sha256:1a3554b0a631c17738d581afca4715588d6d3e332a24ca23a54ce9fd6b8a1ef6                                                          
Status: Downloaded newer image for mongo:latest                                                                                          
Pulling nilipie-api (registry.gitlab.com/haddad/nilipie-api:1.0.0)...                                                                    
1.0.0: Pulling from haddad/nilipie-api                                                                                                   
4fe2ade4980c: Already exists                                                                                                             
36e3ebf48735: Already exists                                                                                                             
2542dc4892a7: Already exists                                                                                                             
57805c670d0d: Pull complete                                                                                                              
a1b088131946: Pull complete                                                                                                              
7e53d4eeba10: Pull complete                                                                                                              
Digest: sha256:308a8b94b9f9bfccf01dff881685e318d7054a1690fca22748c00bf0d7fda2e4                                                          
Status: Downloaded newer image for registry.gitlab.com/haddad/nilipie-api:1.0.0                                                          
Pulling nilipie-web (registry.gitlab.com/haddad/nilipie:1.0.0)...                                                                        
1.0.0: Pulling from haddad/nilipie                                                                                                       
fffd53603a30: Already exists                                                                                                             
4f4fb700ef54: Already exists                                                                                                             
43a58ea360c3: Already exists                                                                                                             
9720f09c28f1: Already exists                                                                                                             
39e3e11b2204: Already exists                                                                                                             
a01c25a9e5d0: Already exists                                                                                                             
90b1830dd253: Already exists                                                                                                             
b548d0ebc7c6: Already exists                                                                                                             
5b92a71edf40: Already exists                                                                                                             
d1ac08c9491d: Already exists                                                                                                             
688ec4ec52d9: Already exists                                                                                                             
fea29cbd54a2: Already exists                                                                                                             
a3723af21029: Already exists                                                                                                             
0c696cae0931: Pull complete                                                                                                              
Digest: sha256:e5f0ab9ed6f9f25720e1bdb05284f8980939db440441602e67ea6b4eb2724ee6                                                          
Status: Downloaded newer image for registry.gitlab.com/haddad/nilipie:1.0.0                                                              
Pulling reverse-proxy (jwilder/nginx-proxy:)...                                                                                          
latest: Pulling from jwilder/nginx-proxy                                                                                                 
be8881be8156: Pull complete                                                                                                              
b4babd36efe5: Pull complete                                                                                                              
f4eba7658e18: Pull complete                                                                                                              
fc141716ac64: Pull complete                                                                                                              
87b964c68304: Pull complete                                                                                                              
d07092114f4c: Pull complete                                                                                                              
5092b1e0c1da: Pull complete                                                                                                              
d90a3596290d: Pull complete                                                                                                              
5ca9f664a671: Pull complete                                                                                                              
eb9b93208683: Pull complete                                                                                                              
Digest: sha256:e869d7aea7c5d4bae95c42267d22c913c46afd2dd8113ebe2a24423926ba1fff                                                          
Status: Downloaded newer image for jwilder/nginx-proxy:latest                                                                            
Creating mongodb ... done                                                                                                                
Creating nilipie-api ... done                                                                                                            
Creating nilipie-web ... done                                                                                                            
Creating git_reverse-proxy_1 ... done
Attaching to mongodb, nilipie-api, nilipie-web, git_reverse-proxy_1
mongodb          | 2018-10-12T13:56:25.584+0000 I CONTROL  [main] Automatically disabling TLS 1.0, to force-enable TLS 1.0 specify --sslDisabledProtocols 'none'
mongodb          | 2018-10-12T13:56:25.593+0000 I CONTROL  [initandlisten] MongoDB starting : pid=1 port=27017 dbpath=/data/db 64-bit host=0652126ade6a
mongodb          | 2018-10-12T13:56:25.593+0000 I CONTROL  [initandlisten] db version v4.0.3
mongodb          | 2018-10-12T13:56:25.593+0000 I CONTROL  [initandlisten] git version: 7ea530946fa7880364d88c8d8b6026bbc9ffa48c
mongodb          | 2018-10-12T13:56:25.593+0000 I CONTROL  [initandlisten] OpenSSL version: OpenSSL 1.0.2g  1 Mar 2016
mongodb          | 2018-10-12T13:56:25.593+0000 I CONTROL  [initandlisten] allocator: tcmalloc
mongodb          | 2018-10-12T13:56:25.593+0000 I CONTROL  [initandlisten] modules: none
mongodb          | 2018-10-12T13:56:25.593+0000 I CONTROL  [initandlisten] build environment:
mongodb          | 2018-10-12T13:56:25.593+0000 I CONTROL  [initandlisten]     distmod: ubuntu1604
mongodb          | 2018-10-12T13:56:25.593+0000 I CONTROL  [initandlisten]     distarch: x86_64
mongodb          | 2018-10-12T13:56:25.593+0000 I CONTROL  [initandlisten]     target_arch: x86_64
mongodb          | 2018-10-12T13:56:25.594+0000 I CONTROL  [initandlisten] options: { net: { bindIpAll: true } }
mongodb          | 2018-10-12T13:56:25.594+0000 I STORAGE  [initandlisten]
mongodb          | 2018-10-12T13:56:25.594+0000 I STORAGE  [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
mongodb          | 2018-10-12T13:56:25.594+0000 I STORAGE  [initandlisten] **          See http://dochub.mongodb.org/core/prodnotes-filesystem
mongodb          | 2018-10-12T13:56:25.594+0000 I STORAGE  [initandlisten] wiredtiger_open config: create,cache_size=4467M,session_max=20000,eviction=(threads_min=4,threads_max=4),config_base=false,statistics=(fast),log=(enabled=true,archive=true,path=journal,compressor=snappy),file_manager=(close_idle_time=100000),statistics_log=(wait=0),verbose=(recovery_progress),
mongodb          | 2018-10-12T13:56:26.408+0000 I STORAGE  [initandlisten] WiredTiger message [1539352586:408380][1:0x7f55577e2a00], txn-recover: Set global recovery timestamp: 0
mongodb          | 2018-10-12T13:56:26.421+0000 I RECOVERY [initandlisten] WiredTiger recoveryTimestamp. Ts: Timestamp(0, 0)
mongodb          | 2018-10-12T13:56:26.438+0000 I CONTROL  [initandlisten]
mongodb          | 2018-10-12T13:56:26.438+0000 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
mongodb          | 2018-10-12T13:56:26.438+0000 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
mongodb          | 2018-10-12T13:56:26.438+0000 I CONTROL  [initandlisten]
mongodb          | 2018-10-12T13:56:26.438+0000 W CONTROL  [initandlisten]
mongodb          | 2018-10-12T13:56:26.438+0000 W CONTROL  [initandlisten]
mongodb          | 2018-10-12T13:56:26.438+0000 I CONTROL  [initandlisten]
mongodb          | 2018-10-12T13:56:26.438+0000 I STORAGE  [initandlisten] createCollection: admin.system.version with provided UUID: b017f0f6-f3f9-4f0d-9815-4bf7f2b2fac6
mongodb          | 2018-10-12T13:56:26.599+0000 I COMMAND  [initandlisten] setting featureCompatibilityVersion to 4.0
mongodb          | 2018-10-12T13:56:26.606+0000 I STORAGE  [initandlisten] createCollection: local.startup_log with generated UUID: af2233c7-6313-4c2c-a3c6-eb7faf323a61
mongodb          | 2018-10-12T13:56:26.645+0000 I FTDC     [initandlisten] Initializing full-time diagnostic data capture with directory '/data/db/diagnostic.data'
nilipie-api      | yarn run v1.9.4
mongodb          | 2018-10-12T13:56:26.649+0000 I NETWORK  [initandlisten] waiting for connections on port 27017
mongodb          | 2018-10-12T13:56:26.649+0000 I STORAGE  [LogicalSessionCacheRefresh] createCollection: config.system.sessions with generated UUID: bda8656e-4ea1-4df6-ba39-13a30467210d
nilipie-api      | $ node app.js
mongodb          | 2018-10-12T13:56:26.692+0000 I INDEX    [LogicalSessionCacheRefresh] build index on: config.system.sessions properties: { v: 2, key: { lastUse: 1 }, name: "lsidTTLIndex", ns: "config.system.sessions", expireAfterSeconds: 1800 }
mongodb          | 2018-10-12T13:56:26.692+0000 I INDEX    [LogicalSessionCacheRefresh]          building index using bulk method; build may temporarily use up to 500 megabytes of RAM
nilipie-api      | DB connected succesfully
nilipie-api      | Nilipie API listening at http://:::80
reverse-proxy_1  | WARNING: /etc/nginx/dhparam/dhparam.pem was not found. A pre-generated dhparam.pem will be used for now while a new one
reverse-proxy_1  | is being generated in the background.  Once the new dhparam.pem is in place, nginx will be reloaded.
mongodb          | 2018-10-12T13:56:26.693+0000 I INDEX    [LogicalSessionCacheRefresh] build index done.  scanned 0 total records. 0 secs
reverse-proxy_1  | forego     | starting dockergen.1 on port 5000
mongodb          | 2018-10-12T13:56:27.908+0000 I NETWORK  [listener] connection accepted from 172.18.0.3:56082 #1 (1 connection now open)
reverse-proxy_1  | forego     | starting nginx.1 on port 5100
mongodb          | 2018-10-12T13:56:27.926+0000 I NETWORK  [conn1] received client metadata from 172.18.0.3:56082 conn1: { driver: { name: "nodejs", version: "2.2.27" }, os: { type: "Linux", name: "linux", architecture: "x64", version: "4.9.93-linuxkit-aufs" }, platform: "Node.js v10.10.0, LE, mongodb-core: 2.1.11" }
reverse-proxy_1  | dockergen.1 | 2018/10/12 13:56:28 Generated '/etc/nginx/conf.d/default.conf' from 26 containers
reverse-proxy_1  | dockergen.1 | 2018/10/12 13:56:28 Running 'nginx -s reload'
reverse-proxy_1  | dockergen.1 | 2018/10/12 13:56:28 Watching docker events
reverse-proxy_1  | dockergen.1 | 2018/10/12 13:56:28 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
reverse-proxy_1  | 2018/10/12 13:56:29 [notice] 52#52: signal process started
reverse-proxy_1  | Generating DH parameters, 2048 bit long safe prime, generator 2
reverse-proxy_1  | This is going to take a long time
reverse-proxy_1  | dhparam generation complete, reloading nginx                                                                                                
                                                                                                                                         

We now have clearly separated the build and the run stages of our SDLC, we have a declarative definition of our cloud native solution in the form of the docker-compse.yml file that can be maintained separately from the source code of the individual components of the solution. This file could belong to a different project that maintains the solution (or project) as a whole, as in the file we point to a particular version of the web and api Docker images (we can do that for all images as well, like MongoDB etc if we want to tie down our project to particular versions of dependencies) when we want to move to different versions of these components we have a new version of this project released that will pull the right versions of components’ Docker images.

This is still a novel set up as we haven’t addressed issues like security, networking, session management or storage for the cloud native architecture but we will cover these in coming articles. Next thing I would like to cover is scaling, how do we scale the application in a cloud infrastructure. We might want to, for example, scale the back end application more than the front end should we deem it necessary which is quite interesting. The tools that allow this are called container orchestration tools, like Docker Swarm or the famous Kubernetes.

In the next article we look at using Kubernetes to orchestrate our app.

 
comments powered by Disqus