GCP + Container Registry: Pushing/Pulling Images

PROBLEM

You want to push a new image to Google Container Registry (GCR) or pull an existing image from GCR.

SOLUTION

Pushing a New Image to GCR

Prepare your Dockerfile.

FROM alpine:3.7

# some content...

Create an image and tag it with a path pointing to GCR within a project.

There are several variations of GCR’s hostname (ex: gcr.io, us.gcr.io, eu.gcr.io, etc) depending on the data center’s location.

The GCR path has the following format: [HOSTNAME]/[PROJECT-ID]/[IMAGE].

docker build -t gcr.io/shitty-project/shitty-repo .

Log into GCP.

gcloud auth login

Register gcloud as a Docker credential helper.

gcloud auth configure-docker

Push the image to GCR.

docker push gcr.io/shitty-project/shitty-repo

View pushed image.

gcloud container images list-tags gcr.io/shitty-project/shitty-repo

DIGEST        TAGS    TIMESTAMP
78b36c0b456d  latest  2019-03-07T16:19:53

The repository and image can also be viewed in GCP Console.

Image in GCR

Pulling an Existing Image from GCR

Proceed with the authentication process if it is not done already.

gcloud auth login
gcloud auth configure-docker

Pull the image from GCR.

docker pull gcr.io/shitty-project/shitty-repo

Ansible: Handling Multiple Hosts via SSH

PROBLEM

To run Ansible playbook in multiple hosts via SSH.

SOLUTION

Configuring SSH environment

Ensure SSH keypair exists on the current machine (ex: ~/.ssh/id_rsa for private key and ~/.ssh/id_rsa.pub for public key). If you do not have one, create one:

ssh-keygen

Copy the public key (ex: ~/.ssh/id_rsa.pub) to each remote host’s ~/.ssh/authorized_keys. If this file doesn’t exist, create it.

Ensure the current machine’s .ssh/ directory and file have correct permission.

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

In the current machine’s /etc/hosts, add all remote hosts.

127.0.0.1      localhost  # current machine
192.168.1.100  donkeykong # remote host 1
192.168.1.200  supermario # remote host 2

In each remote host, enable the remote login and grant yourself the access to this service.

Enabling Remote Login on Mac

Test SSH connection to remote host to ensure they work first before working on Ansible playbook.

ssh user@donkeykong
ssh user@supermario

Creating Ansible Playbook

Create ansible.cfg and define the location of inventory file.

[defaults]
inventory = inventory.yml

Create inventory.yml and define both localhost and remote hosts.

all:
  hosts:
    localhost:
      ansible_connection: local
    donkeykong:
      ansible_user: user
      ansible_ssh_private_key_file: ~/.ssh/id_rsa
    supermario:
      ansible_user: user
      ansible_ssh_private_key_file: ~/.ssh/id_rsa

Run a test to ensure the connection to remote hosts are successful.

ansible all -i inventory.yml -m ping

If successful, the output looks something like this:

localhost | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
donkeykong | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
supermario | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Create main.yml with a very simple task.

- name: all-hosts
  hosts: all
  tasks:
    - name: Capture current dir
      shell: pwd
      register: output

    - name: Display output
      debug: msg='{{ output.stdout }}'

Run the playbook.

ansible-playbook main.yml

If successful, the output looks something like this:

PLAY [all-hosts] *******************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [localhost]
ok: [donkeykong]
ok: [supermario]

TASK [Capture current dir] *********************************************************************************************
changed: [localhost]
changed: [donkeykong]
changed: [supermario]

TASK [Display output] **************************************************************************************************
ok: [localhost] => {
    "msg": "/Users/user/myshittycode"
}
ok: [donkeykong] => {
    "msg": "/Users/user"
}
ok: [supermario] => {
    "msg": "/Users/user"
}

PLAY RECAP *************************************************************************************************************
donkeykong                 : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
supermario                 : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Controlling the Hosts

Sometimes, you want finer controls on what tasks to be ran in certain hosts.

To run in just one host (ex: donkeykong):

- name: one-host
  hosts: donkeykong
  tasks:
    - ...

To run in all remote hosts except localhost:

- name: all-hosts-except-localhost
  hosts: all:!localhost
  tasks:
    - ...

Squid: Configuring Whitelisted URLs

PROBLEM

To configure a proxy server that only allows whitelisted URLs through.

SOLUTION

Install Squid… in this case, on Ubuntu.

sudo apt install -y squid

Ensure the service is running.

my@shittycode:/etc/squid$ sudo systemctl status squid
● squid.service - Squid Web Proxy Server
Loaded: loaded (/lib/systemd/system/squid.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2021-04-21 17:20:54 CDT; 3min 3s ago
Docs: man:squid(8)
Process: 9008 ExecStartPre=/usr/sbin/squid --foreground -z (code=exited, status=0/SUCCESS)
Process: 9012 ExecStart=/usr/sbin/squid -sYC (code=exited, status=0/SUCCESS)
Main PID: 9013 (squid)
Tasks: 4 (limit: 44379)
Memory: 16.1M
CGroup: /system.slice/squid.service
├─9013 /usr/sbin/squid -sYC
├─9015 (squid-1) --kid squid-1 -sYC
├─9016 (logfile-daemon) /var/log/squid/access.log
└─9017 (pinger)

Apr 21 17:20:54 shittycode squid[9015]: Max Swap size: 0 KB
Apr 21 17:20:54 shittycode squid[9015]: Using Least Load store dir selection
Apr 21 17:20:54 shittycode squid[9015]: Set Current Directory to /var/spool/squid
Apr 21 17:20:54 shittycode squid[9015]: Finished loading MIME types and icons.
Apr 21 17:20:54 shittycode squid[9015]: HTCP Disabled.
Apr 21 17:20:54 shittycode squid[9015]: Pinger socket opened on FD 14
Apr 21 17:20:54 shittycode squid[9015]: Squid plugin modules loaded: 0
Apr 21 17:20:54 shittycode squid[9015]: Adaptation support is off.
Apr 21 17:20:54 shittycode squid[9015]: Accepting HTTP Socket connections at local=[::]:3128 remote=[::] FD 12 flags=9
Apr 21 17:20:55 shittycode squid[9015]: storeLateRelease: released 0 objects

Create a file ( /etc/squid/whitelist.txt ) containing the whitelisted URLs. In this example, only one URL is whitelisted.

my@shittycode:/etc/squid$ cat whitelist.txt
www.google.com

To simplify the configuration, backup /etc/squid/squid.conf and create the same file with these minimal configurations.

my@shittycode:/etc/squid$ cat squid.conf

# An ACL named 'whitelist'
acl whitelist dstdomain '/etc/squid/whitelist.txt'

# Allow whitelisted URLs through
http_access allow whitelist

# Block the rest
http_access deny all

# Default port
http_port 3128

Restart the service to pick up the change.

sudo systemctl restart squid

To test the configuration, when hitting a non-whitelisted URL, it should return 403.

my@shittycode:/etc/squid$ curl -x localhost:3128 -I -L yahoo.com
HTTP/1.1 403 Forbidden
Server: squid/4.10
Mime-Version: 1.0
Date: Wed, 21 Apr 2021 22:22:02 GMT
Content-Type: text/html;charset=utf-8
Content-Length: 3507
X-Squid-Error: ERR_ACCESS_DENIED 0
Vary: Accept-Language
Content-Language: en
X-Cache: MISS from shittycode
X-Cache-Lookup: NONE from shittycode:3128
Via: 1.1 shittycode (squid/4.10)
Connection: keep-alive

When hitting a whitelisted URL, it should return 200.

my@shittycode:/etc/squid$ curl -x localhost:3128 -I -L www.google.com
HTTP/1.1 200 OK
Content-Type: text/html; charset=ISO-8859-1
P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
Date: Wed, 21 Apr 2021 22:21:03 GMT
Server: gws
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
Expires: Wed, 21 Apr 2021 22:21:03 GMT
Cache-Control: private
Set-Cookie: 1P_JAR=2021-04-21-22; expires=Fri, 21-May-2021 22:21:03 GMT; path=/; domain=.google.com; Secure
Set-Cookie: NID=214=AAK1Z6cV4cXlOGLIdHrKhiyzW2iBKpkN5-3OXvVrxEGrw-VekbvM1uFMMUAGubhAciT8NcyCVto2fpDPHJXRBECcqJRFTsUDNb3WBUNIgvK0zWpyxz8bl1aSqB22nQhf2fEwfDM9nAkVZyQG8rst054qOfAHO9kDvkrZRWn9HyM; expires=Thu, 21-Oct-2021 22:21:03 GMT; path=/; domain=.google.com; HttpOnly
X-Cache: MISS from shittycode
X-Cache-Lookup: MISS from shittycode:3128
Via: 1.1 shittycode (squid/4.10)
Connection: keep-alive

macOS Big Sur: Poor Screen Quality When Connecting to Old Monitor via HDMI

PROBLEM

You have a shiny Mac laptop running macOS Big Sur. This laptop is connected to an old external monitor via HDMI. The screen quality looks pixelated and fuzzy.

Running font smoothing (as below) doesn’t fix the problem:

defaults -currentHost write -g AppleFontSmoothing -int 3

You are poor enough to buy a new 4K monitor.

SOLUTION

The usage of HDMI seems to fool macOS Big Sur, thinking your old monitor is a glorious new TV. This causes macOS to use YPbPr instead of RGB mode.

The fix is to perform Extended Display Identification Data (EDID) override to force macOS to use RGB mode.

Some instructions on the web are inaccurate. There’s no need to reboot into Recovery Mode to disable System Integrity Protection (SIP) via csrutil first.

Instead, run the following command:

sudo mkdir -p /Library/Displays/Contents/Resources/Overrides
cd /Library/Displays/Contents/Resources/Overrides
sudo curl -O https://gist.githubusercontent.com/ejdyksen/8302862/raw/patch-edid.rb
sudo ruby patch-edid.rb

Reboot the laptop.

Docker: Executing Startup Script When Running Container Interactively

PROBLEM

When running the Docker container interactively (ex: docker run --rm -it myimage), you want to run a startup script every time.

SOLUTION

For Ubuntu, Debian and Centos images, write the startup script to /root/.bashrc:

# UBUNTU
FROM ubuntu:latest
RUN echo "echo 'Welcome!'" >> /root/.bashrc
WORKDIR /home

# DEBIAN
FROM debian:latest
RUN echo "echo 'Welcome!'" >> /root/.bashrc
WORKDIR /home

# CENTOS
FROM centos:latest
RUN echo "echo 'Welcome!'" >> /root/.bashrc
WORKDIR /home

For Alpine image, it’s a little different because it uses Ash shell. Besides writing the startup script to /root/.profile, you also need to set that path to an environment variable called ENV:

FROM alpine:latest
ENV ENV=/root/.profile
RUN echo "echo 'Welcome!'" > $ENV
WORKDIR /home