visit
Deploying a web application can be a challenging task, especially if you want to ensure that your application can handle high traffic, provide high availability, and comply with security and regulatory standards. Fortunately, there are tools and platforms that can help you achieve these goals, such as Aptible and Kubernetes.
Aptible is a platform that simplifies the deployment and management of secure and compliant web applications. Aptible provides features such as automated provisioning, encryption, backups, logging, monitoring, and auditing for your web applications. Aptible also helps you comply with various such as HIPAA, SOC 2, ISO 27001, and GDPR.
Learn why Aptible's Engineering-Led Support and Production-Ready SLAs makes them a great choice for you. Read here
Kubernetes is an open-source system that automates the deployment, scaling, and management of containerized applications. Kubernetes allows you to run your web application in a cluster of servers, called nodes, that can be distributed across different regions and providers. Kubernetes also provides features such as , service discovery, self-healing, , rolling updates, and more.
Pods: The smallest unit of deployment in Kubernetes. A pod is a group of one or more containers that share the same network and storage resources.
Services: An abstraction that defines a logical set of pods and a policy to access them. A service provides a stable endpoint for your pods, regardless of their location or state.
Deployments: An abstraction that manages the creation and update of pods. A deployment allows you to specify the desired state of your pods, such as the number of replicas, the image version, the update strategy, etc. Kubernetes will ensure that your pods match the desired state.
Aptible Environments: A logical grouping of resources in Aptible. An environment consists of one or more apps, which are the web applications that you deploy with Aptible; one or more databases, which are the data stores that you provision with Aptible; and one or more endpoints, which are the entry points for accessing your apps and databases.
Prerequisites
Step 1: Create an Aptible app and configure your environment variables
Step 2: Build and push your web application image to a container registry
Step 3: Create a Kubernetes deployment and service for your web application
Step 4: Expose your web application service to the internet with Aptible endpoints
Step 5: Test and verify your web application deployment
Conclusion
You can use either the or the Aptible dashboard to create an app and configure your environment variables. In this article, we will use the Aptible CLI, which is a command-line tool that allows you to interact with Aptible.
To create an app with the Aptible CLI, you need to:
Log in to your Aptible account using the aptible login
command. You will be prompted to enter your email and password, and then a verification code sent to your email.
Choose an environment for your app using the aptible switch
command. An environment is a logical grouping of resources in Aptible, such as apps, databases, and endpoints. You can have multiple environments for different purposes, such as development, staging, or production. In this article, we will use the production
environment, which is created by default when you sign up for Aptible.
Create an app using the aptible apps:create
command. You need to provide a name for your app, which must be unique within your environment. In this article, we will name our app web-app.
$ aptible login
Email: [email protected]
Password: ********
Verification code: 123456
Logged in as [email protected]
$ aptible switch production
Switched to production
$ aptible apps:create web-app
App web-app created!
- List the existing environment variables for your app using the aptible config
command. You will see some default environment variables that are set by Aptible, such as APTIBLE_APP_ID
, APTIBLE_APP_HANDLE
, etc.
- Add new environment variables for your app using the aptible config:set
command. You need to provide the key and value for each environment variable, separated by a space. You can add multiple environment variables at once by separating them with a space as well. In this article, we will add some environment variables that are required by our web application, such as DATABASE_URL
, SECRET_KEY_BASE
, RAILS_ENV
, etc.
- Verify that your environment variables are set correctly using the aptible config
command again.
$ aptible config --app web-app
APTIBLE_APP_ID=123
APTIBLE_APP_HANDLE=web-app
...
$ aptible config:set --app web-app
DATABASE_URL=postgres://user:password@host:port/db SECRET_KEY_BASE=abcdefg
RAILS_ENV=production
DATABASE_URL=postgres://user:password@host:port/db
SECRET_KEY_BASE=abcdefg
RAILS_ENV=production
$ aptible config --app web-app
APTIBLE_APP_ID=123
APTIBLE_APP_HANDLE=web-app
DATABASE_URL=postgres://user:password@host:port/db
SECRET_KEY_BASE=abcdefg
RAILS_ENV=production
...
You can use Docker or any other similar tool to build your web application image and tag it with a version number. Docker is a software that allows you to create, run, and share containers. A container is an isolated environment that runs your web application image. You can install Docker here.
- Navigate to the directory where your web application codebase and Dockerfile are located using the cd
command.
- Build your web application image using the docker build
command. You need to provide a name and a tag for your image, which must be unique within your container registry. The name and tag are separated by a colon (:
). In this article, we will name our image web-app
and tag it with v1
.
- Verify that your web application image is built correctly using the docker images
command. You will see your image listed along with its name, tag, size, and creation date.
$ cd web-app
$ docker build -t web-app:v1 .
Sending build context to Docker daemon 2.048kB
Step 1/8 : FROM ruby:2.7
...
Successfully built 123456789abc
Successfully tagged web-app:v1
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
web-app v1 123456789abc 10 seconds ago 1.23GB
- Log in to your container registry account using the docker login
command. You will be prompted to enter your username and password, and then a verification code if required.
- Push your web application image using the docker push
command. You need to provide the name and tag of your image, as well as the name of your container registry. The name of your container registry is usually prefixed to the name of your image, separated by a slash (/
). In this article, we will use Docker Hub as our container registry, which is a public service that hosts and distributes Docker images. We will prefix our image name with our Docker Hub username, which is user
.
- Verify that your web application image is pushed correctly using the docker images
command again. You will see a message indicating that your image was pushed successfully.
$ docker login
Username: user
Password: ********
Verification code: 123456
Login Succeeded
$ docker push user/web-app:v1
The push refers to repository
[docker.io/user/web-app]
...
123456789abc: Pushed
v1: digest: sha256:abcdefg size: 1234
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
user/web-app v1 123456789abc 10 seconds ago 1.23GB
Alternatively, you can use another container registry of your choice, such as Google Container Registry (GCR), Amazon Elastic Container Registry (ECR), Microsoft Azure Container Registry (ACR), etc. Each container registry may have different requirements and steps for logging in, pushing, and pulling images. You can refer to their respective documentation for more details.
You can use YAML files to define your Kubernetes deployment and service objects for your web application. YAML is a human-readable data format that allows you to specify the configuration and properties of your Kubernetes objects. You can use any text editor to create and edit YAML files.
To define your Kubernetes deployment object with a YAML file, you need to:
- Create a file with a .yaml
or .yml
extension, such as deployment.yaml
.
- Specify the apiVersion
, kind
, metadata
, and spec
fields for your deployment object.
The apiVersion
field indicates the version of the Kubernetes API that you are using. In this article, we will use apps/v1
, which is the latest stable version for deployments.
The kind
field indicates the type of the Kubernetes object. In this case, it is Deployment
.
The metadata
field contains information about the name, labels, annotations, etc. of your deployment object. Labels are key-value pairs that can be used to identify and group your Kubernetes objects. Annotations are key-value pairs that can be used to store additional information or metadata about your Kubernetes objects.
The spec
field contains the desired state and behavior of your deployment object, such as the number of replicas, the pod template, the update strategy, etc.
Specify the selector
, template
, and eplicas
fields within the spec
field for your deployment object. These fields are required for any deployment object.
The selector
field defines how to select the pods that belong to your deployment object. You need to provide a matchLabels
field that contains one or more labels that match the labels of your pods.
The emplate
field defines the pod template that will be used to create new pods for your deployment object. You need to provide a metadata
field that contains the labels of your pods, and a spec
field that contains the configuration and properties of your pods, such as the containers, volumes, environment variables, etc.
The replicas
field defines the number of pods that you want to run for your deployment object. You can specify any positive integer value. In this article, we will use 3 replicas for our deployment object.
- Specify the containers
, image
, and ports
fields within the spec
field of the pod template for your deployment object.
The containers
field defines one or more containers that will run in your pod. You need to provide a name and an image for each container. The name can be any string that identifies your container within your pod. The image is the name and tag of your web application image that you pushed to your container registry in the previous step. You need to prefix the image name with the name of your container registry, separated by a slash (/
). In this article, we will prefix our image name with our Docker Hub username, which is user
.
The image
field defines the name and tag of your web application image that you pushed to your container registry in the previous step. You need to prefix the image name with the name of your container registry, separated by a slash (/
).
- The ports
field defines one or more ports that will be exposed by your container. You need to provide a name and a containerPort for each port. The name can be any string that identifies your port within your container. The containerPort is the port number that your web application listens on within your container. In this article, we will use 3000 as our containerPort for our web application.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app-deployment
labels:
app: web-app
spec:
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web-app-container
image: user/web-app:v1
ports:
- name: http
containerPort: 3000
replicas:3
To define your Kubernetes service object with a YAML file, you need to:
- Create a file with a .yaml
or .yml
extension, such as service.yaml
.
- Specify the apiVersion
, kind
, metadata
, and spec
fields for your service object. These fields are required for any Kubernetes object.
• The mapiVersion
field indicates the version of the Kubernetes API that you are using. In this article, we will use v1
, which is the latest stable version for services.
• The kind
field indicates the type of the Kubernetes object. In this case, it is Service
.
• The metadata
field contains information about the name, labels, annotations, etc. of your service object. Labels are key-value pairs that can be used to identify and group your Kubernetes objects. Annotations are key-value pairs that can be used to store additional information or metadata about your Kubernetes objects.
• The spec
field contains the desired state and behavior of your service object, such as the type, ports, selector, etc.
- Specify the type
, ports
, and selector
fields within the spec
field for your service object. These fields are required for any service object.
• The type
field defines the type of your service object, which determines how your service will be exposed to the outside world. There are four types of services: ClusterIP, NodePort, LoadBalancer, and ExternalName. In this article, we will use ClusterIP as our service type, which is the default type that creates a virtual IP address within the cluster that can be used to access your pods.
• The ports
field defines one or more ports that will be exposed by your service. You need to provide a name, a port, and a targetPort for each port. The name can be any string that identifies your port within your service. The port is the port number that your service will expose to the outside world. The targetPort is the port number that your service will forward the traffic to within your pod. In this article, we will use 80 as our port and 3000 as our targetPort for our web application.
• The selector
field defines how to select the pods that belong to your service object. You need to provide one or more labels that match the labels of your pods.
apiVersion: v1
kind: Service
metadata:
name: web-app-service
labels:
app: web-app
spec:
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 3000
selector:
app: web-app
To apply your YAML files to your Kubernetes cluster, you need to use kubectl
.
- Navigate to the directory where your YAML files are located using the cd
command.
- Apply your YAML files using the kubectl apply
command. You need to provide the name of your YAML file or a directory that contains multiple YAML files as an argument. You can also use the -f
flag to specify the file or directory name. In this article, we will use a directory named k8s
that contains our YAML files for our deployment and service objects.
- Verify that your deployment and service objects are created correctly using the kubectl get
command. You can provide the name of your object or a type of object as an argument. You can also use the -o wide
flag to get more details about your objects.
$ cd k8s
$ kubectl apply -f .
deployment.apps/web-app-deployment created
$ kubectl get deployment web-app-deployment -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
web-app-deployment 3/3 3 3 10s web-app-container user/web-app:v1 app=web-app
$ kubectl get service web-app-service -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
web-app-service ClusterIP 10.100.200.300 <none> 80/TCP 10s app=web-app
You can use the Aptible CLI to create an endpoint for your web application service.
To create an endpoint with the Aptible CLI, you need to:
- Create an endpoint using the aptible endpoints:create
command. You need to provide a name for your endpoint, which must be unique within your environment. You also need to provide some options for your endpoint, such as the protocol, port, hostname, path, etc.
• The --type
option defines the type of your endpoint, which determines how your endpoint will be exposed to the outside world. There are two types of endpoints: TCP and HTTPS. In this article, we will use HTTPS as our endpoint type, which is the default type that creates a secure and encrypted connection between your web application service and your clients.
• The --internal
option defines whether your endpoint will be internal or external. An internal endpoint can only be accessed from within your Aptible environment, while an external endpoint can be accessed from anywhere on the internet. In this article, we will use an external endpoint, which is the default option.
• The --port
option defines the port number that your endpoint will listen on. You need to provide a port number that matches the port number that your web application service exposes. In this article, we will use 80 as our port number for our web application service.
• The --hostname
option defines the hostname that your endpoint will use. You can provide a custom domain name that you own and have configured with DNS records, or use a default domain name that is provided by Aptible. In this article, we will use a custom domain name that we own and have configured with DNS records, which is web-app.example.com.
• The --path
option defines the path that your endpoint will use. You can provide a specific path that you want to route to your web application service, or use a wildcard path that matches any path. In this article, we will use a wildcard path that matches any path, which is /
.
- Verify that your endpoint is created correctly using the aptible endpoints:list
command. You will see your endpoint listed along with its name, type, internal flag, port, hostname, path, etc.
$ aptible switch production
Switched to production
$ aptible endpoints:create --type https --internal false --port 80 --hostname web-app.example.com --path / web-app
Endpoint web-app created!
$ aptible endpoints:list --app web-app
NAME TYPE INTERNAL PORT HOSTNAME PATH
web-app HTTPS false 80 web-app.example.com /
Aptible will automatically provide and manage SSL certificates for your endpoint using Let's Encrypt, which is a free and open certificate authority that provides domain validation certificates. You do not need to do anything to enable SSL encryption for your endpoint. However, you need to make sure that your custom domain name is configured with DNS records that point to your Aptible environment. You can find the DNS records for your environment on the Aptible dashboard under the Domains tab.
After you have exposed your web application service to the internet with Aptible endpoints, you need to test and verify your web application deployment. You can use curl
to send requests to your web application endpoint and check the responses. You can also use kubectl
to monitor the status and logs of your web application pods and service.
- Use the curl
command with the URL of your web application endpoint as an argument. The URL is composed of the protocol, hostname, and path of your endpoint. In this article, we will use //web-app.example.com/
as our URL for our web application endpoint.
$ curl //web-app.example.com/
HTTP/1.1 200 OK
Server: nginx/1.19.10
Date: Tue, 10 Oct 2023 19:19:51 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 1234
Connection: keep-alive
X-Powered-By: Aptible
<html>
<head>
<title>Web App</title>
</head>
<body>
<h1>Welcome to Web App!</h1>
<p>This is a sample web application deployed with Aptible and Kubernetes.</p>
</body>
</html>
- Use the kubectl get
command with the name or type of your object as an argument. You can also use the `-o wide` flag to get more details about your object, such as the node name, pod IP, labels, etc.
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-app-deployment-f4c6d9b4f-4jw9m 1/1 Running 0 5m23s 10.100.200.301 node-1 <none> <none>
web-app-deployment-f4c6d9b4f-hx7kq 1/1 Running 0 5m23s 10.100.200.302 node-2 <none> <none>
web-app-deployment-f4c6d9b4f-z9v7n 1/1 Running 0 5m23s 10.100.200.303 node-3 <none> <none>
$ kubectl get service -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
web-app-service ClusterIP 10.100.200.300 <none> 80/TCP 5m23s app=web-app
To monitor the logs of your web application pods with kubectl, you need to:
- Use the kubectl logs
command with the name of your pod as an argument. You can also use the -f
flag to follow the logs in real time, or the --since
flag to specify a time range for the logs.
$ kubectl logs web-app-deployment-f4c6d9b4f-4jw9m -f --since=10m
[2023-10-10 19:19:51] INFO WEBrick 1.6.0
[2023-10-10 19:19:51] INFO ruby 2.7.0 (2019-12-25) [x86_64-linux]
[2023-10-10 19:19:51] INFO WEBrick::HTTPServer#start: pid=1 port=3000
I, [2023-10-10T19:20:12.345678 #1] INFO -- : Started GET "/" for 10.100.200.400 at 2023-10-10 19:20:12 +0000
I, [2023-10-10T19:20:12.456789 #1] INFO -- : Processing by HomeController#index as HTML
I, [2023-10-10T19:20:12.567890 #1] INFO -- : Rendering home/index.html.erb within layouts/application
I, [2023-10-10T19:20:12.678901 #1] INFO -- : Rendered home/index.html.erb within layouts/application (Duration: 11.0ms | Allocations: 1234)
I, [2023-10-10T19:20:12.789012 #1] INFO -- : Completed 200 OK in 33ms (Views: 22.0ms | ActiveRecord: 0.0ms | Allocations: 4567)