A Universal Installation Script for Google Chrome on Amazon Linux and CentOS
A Universal Installation Script for Google Chrome on Amazon Linux and CentOS
The easiest way to install the latest Chrome version on RHEL, CentOS, and Amazon Linux versions 6.X and 7.X.
# This installs Chrome on any RHEL/CentOS/Amazon Linux variant.
curl https://intoli.com/install-google-chrome.sh | bash
A Universal Installation Script for Google Chrome on Amazon Linux and CentOS 6
CentOS, Amazon Linux AMI, and Red Hat Enterprise Linux are three closely related GNU/Linux distributions which are all popular choices for server installations.
They offer excellent performance and stability, but package availability can often be lacking.
The Extra Packages for Enterprise Linux (EPEL),
a community maintained repository of additional packages, significantly
improves the situation, but doesn’t include Google Chrome/Chromium or a
lot of other software that you would expect on more desktop-oriented
distributions.
This can be a bit frustrating when you’re interested in running headless
Chrome
instances in the cloud for testing or web scraping.
Google does maintain a repository with a Chrome RPM package, but it
only works with RHEL/CentOS 7.X versions.
Amazon Linux is currently only available as a 6.X version, and the 6.X
versions of RHEL/CentOS remain fairly common (in part due to the
transition from
upstart
to systemd
in 7.X).
If you’re using one of these versions then you’re kind of on your own…
and the installation process is quite complicated due to the lack of GTK
3 on RHEL 6.X.
We use headless Chrome instances for web scraping on Amazon Linux here at Intoli
and so we maintain our own package which bundles the otherwise missing
dependencies.
In this article, we’ll show you both how to get up and running as
quickly as possible with our modified Chrome RPM and explain the process
of modifying RPMs to include additional libraries.
If you’ve been searching around for how to run Chrome on Amazon Linux
then hopefully you’ll find this useful!
The Easy Way
Universal Installation Script for RHEL/CentOS 6.X/7.X
All you need to do is run our installer script and you should be good to go.
curl https://intoli.com/install-google-chrome.sh | bash
This will automatically configure and enable the official Google
repository, import Google’s signing key, and install the latest
google-chrome-stable
executable in your path.
If you’re on a RHEL 6.X flavor the it will also automatically find and
install all of the unmet dependencies that would normally make the
installation fail.
You can rerun the script whenever you want to grab the latest version of
Google Chrome and, if you’re using RHEL 7.X, then you can update the
package using yum
as you would with any other package.RHEL/CentOS 7.X
Alternatively, you can add the repository manually if you’re using a 7.X version.
If you’re using Amazon Linux AMI then you’re definitely on a 6.X version and need to use the installation script.
Otherwise, you can run
cat /etc/redhat-release
and check the output for the version number to confirm that you’re using 7.X.
For example, if you’re using RHEL 7.0 then you would expect to see something like:
Red Hat Enterprise Linux Server release 7.0 (Maipo)
You can use the official Google repository if you see a 7.X version there.
To add it to your system, simply create a file called
/etc/yum.repos.d/google-chrome.repo
with the following contents.
[google-chrome]
name=google-chrome
baseurl=http://dl.google.com/linux/chrome/rpm/stable/$basearch
enabled=1
gpgcheck=1
gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub
Then you just need to run
sudo yum install google-chrome-stable
to install the Chrome package.
This will add the
google-chrome-stable
script in /usr/bin/
.
Note that this is the same thing that the installation script does on
7.X flavors, so it’s safe to use the script regardless of which version
you’re on.The Hard Way
If you just want to install Google Chrome on a RHEL variant then I
highly recommend using our installation script in the previous section.
That said, I’ll walk through the process of doing it manually because
some of our more technical readers might find it interesting.
The basic idea is to use the 7.X Google Chrome RPM as a starting point
and then to install the dependencies and extras that we need in order
for it to work on 6.X versions.
This process is exactly the same as the one employed by the installation
script above.
Getting the RPM
First, we’ll grab the official RPM file for the 7.X variants.
It won’t work out of the box on 6.X versions, but it gives us a good starting point for making changes.
We can download the file by creating the same
/etc/yum.repos.d/google-chrome.repo
file that we would use with the 7.X versions
[google-chrome]
name=google-chrome
baseurl=http://dl.google.com/linux/chrome/rpm/stable/$basearch
enabled=1
gpgcheck=1
gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub
and then running:
# install the yum utilities
sudo yum install yum-utils
# download the google-chrome-stable package
yumdownloader google-chrome-stable
The second command should download the RPM file to the current working directory.
The current version has a file name of
google-chrome-stable-63.0.3239.108-1.x86_64.rpm
, but this will change as new versions get released.Installing the (Broken) Package
Let’s start by installing as many of the dependencies of
google-chrome-stable
as we can.
This can be done by using repoquery
, another tool provided by yum-utils
, to resolve the dependency packages and then passing these as arguments to yum
using xargs
.repoquery --requires --resolve google-chrome-stable | xargs sudo yum -y install
After these finish installing, we can try to install the downloaded RPM.
sudo rpm -i google-chrome-stable-63.0.3239.108-1.x86_64.rpm
This will output a list of dependencies that aren’t available on the system.
error: Failed dependencies:
libXss.so.1()(64bit) is needed by google-chrome-stable-63.0.3239.108-1.x86_64
libatk-1.0.so.0()(64bit) is needed by google-chrome-stable-63.0.3239.108-1.x86_64
libgconf-2.so.4()(64bit) is needed by google-chrome-stable-63.0.3239.108-1.x86_64
libgdk-3.so.0()(64bit) is needed by google-chrome-stable-63.0.3239.108-1.x86_64
libgdk_pixbuf-2.0.so.0()(64bit) is needed by google-chrome-stable-63.0.3239.108-1.x86_64
libgtk-3.so.0()(64bit) is needed by google-chrome-stable-63.0.3239.108-1.x86_64
xdg-utils is needed by google-chrome-stable-63.0.3239.108-1.x86_64
Let’s go ahead and install the package anyway, this time skipping the missing
dependencies with the
--nodeps
option.
sudo rpm -i --nodeps google-chrome-stable-63.0.3239.108-1.x86_64.rpm
You’ll likely see an error like
Error: Could not find xdg-icon-resource
warning: %post(google-chrome-stable-63.0.3239.108-1.x86_64) scriptlet failed, exit status 1
because the RPM’s installation script includes the following code for installing the icons using
xdg-icon-resource
.# Add icons to the system icons
XDG_ICON_RESOURCE="`which xdg-icon-resource 2> /dev/null || true`"
if [ ! -x "$XDG_ICON_RESOURCE" ]; then
echo "Error: Could not find xdg-icon-resource" >&2
exit 1
fi
for icon in "/opt/google/chrome/product_logo_"*.png; do
size="${icon##*/product_logo_}"
"$XDG_ICON_RESOURCE" install --size "${size%%.png}" "$icon" "google-chrome"
done
This isn’t anything to worry about, the installation most likely still worked even if you saw this error.
If you run
google-chrome-stable
at this point, however, you’ll see an error like this.
/usr/bin/google-chrome-stable: error while loading shared libraries: libgconf-2.so.4: cannot open shared object file: No such file or directory
This one is more serious.
Note that this
libgconf-2.so.4
was one of the missing dependencies that was listed before we forced the installation of the RPM.
It turns out that we actually do need those dependencies after all!Providing the Missing Dependencies
Running the Chrome executable told us one missing library, but we can use
ldd
to get a more complete picture of the missing shared libraries that google-chrome-stable
links to.
ldd /opt/google/chrome/chrome | grep "not found"
This command will output the list of missing libraries, all of which are ones that we removed as requirements.
libgconf-2.so.4 => not found
libXss.so.1 => not found
libatk-1.0.so.0 => not found
libgtk-3.so.0 => not found
libgdk-3.so.0 => not found
libgdk_pixbuf-2.0.so.0 => not found
We could try to build these libraries ourselves, or even to create
new packages for them, but this would end up being an immense amount of
work because each of these will have their own sets of dependencies.
A much easier solution is to grab these files from another GNU/Linux
system where the same version of Google Chrome is installed.
This is a bit of a hack, but it’s the easiest way to get things working
quickly.
There are two questions to answer here: where to get the libraries from and where to put them.
Let’s start with where to put them because there’s an easy answer.
One obvious choice would be
/usr/lib64/
, but we would eventually end up with some library files that are in conflict with existing system libraries.
Plus it’s kind of gross to dump files that aren’t controlled by the package manager into those system directories.
What are we, Mac users?
If we look inside of the
/usr/bin/google-chrome-stable
script, we can see that it actually prepends a few directories of its own to LD_LIBRARY_PATH
before it launches Chrome.# Always use our versions of ffmpeg libs.
# This also makes RPMs find the compatibly-named library symlinks.
if [[ -n "$LD_LIBRARY_PATH" ]]; then
LD_LIBRARY_PATH="$HERE:$HERE/lib:$LD_LIBRARY_PATH"
else
LD_LIBRARY_PATH="$HERE:$HERE/lib"
fi
export LD_LIBRARY_PATH
With the standard Chrome installation location, the extra library paths are
/opt/google/chrome/
and /opt/google/chrome/lib/
.
These directories will be searched for dependencies before any of the
system directories and they’re nicely colocated with the Chrome
installation.
The /opt/google/chrome/lib/
directory is actually non-existent in the package, but it’s a convenient location to place all of our library files.
Now we’re ready for the harder question: where do we get the libraries from?
Well, given that the Chrome RPM can be installed successfully on CentOS 7, that seems like a good place to look.
The same
repoquery
utility that we used earlier makes it possible to query remote repositories to find the RPMs that provide certain files.
Let’s take the missing libgconf-2.so.4
for example.# Search the CentOS 7 repository for the missing file.
repoquery --repofrompath=centos7,http://mirror.centos.org/centos/7/os/`arch` \
--repoid=centos7 \ # Only consider this specific repository.
--qf="%{location}" \ # Output the URL of the RPM file.
--whatprovides libgconf-2.so.4 # Find the package that provides this library.
This command will output
http://mirror.centos.org/centos/7/os/x86_64/Packages/GConf2-3.2.6-8.el7.i686.rpm
which is so close, but not quite right.
You can see from the
/x86_64/
in the URL that the architecture for this repository for 64 bit, but the .i686.rpm
indicates that this is the 32 bit package.
We’ll need to manually change the suffix to .x86_64.rpm
to specify the correct architecture for our CentOS 6 system (assuming that you’re using a 64 bit version).
After modifying the URL, we can download the RPM and extract it by running the following.
# Change to a temporary directory.
mkdir -p /tmp/working-directory/
cd /tmp/working-directory/
# Download the RPM.
wget http://mirror.centos.org/centos/7/os/x86_64/Packages/GConf2-3.2.6-8.el7.x86_64.rpm
# Extract it.
rpm2cpio GConf2-3.2.6-8.el7.x86_64.rpm | cpio -idmv
This will dump all of the files included in the package into the current working directory.
We only really care about a couple of those files and we can find them by running
find . | grep libgconf-2.so.4
which will output the matching libraries.
./usr/lib64/libgconf-2.so.4.1.5
./usr/lib64/libgconf-2.so.4
The
libgconf-2.so.4
file is the one that ldd
was looking for, but it’s actually a symlink to libgconf-2.so.4.1.5
so we need both.
Let’s copy these to our /opt/google/chrome/lib/
directory and try ldd
again.# Make the directory because it didn't exist already.
sudo mkdir /opt/google/chrome/lib/
# Copy the files over.
sudo cp ./usr/lib64/libgconf-2.so.4 /opt/google/chrome/lib/
sudo cp ./usr/lib64/libgconf-2.so.4.1.5 /opt/google/chrome/lib/
# Add this directory to LD_LIBRARY_PATH and run `ldd`.
export LD_LIBRARY_PATH=/opt/google/chrome/lib/:$LD_LIBRARY_PATH
ldd /opt/google/chrome/chrome | grep "not found"
Note that we’re explicitly adding the directory to
LD_LIBRARY_PATH
so that ldd
knows about it.
This happens automatically when we run google-chrome-stable
, but we need to make sure we add it ourselves if we want to use these libraries outside of that context.
Anyway, ldd
will now output a revised list of missing depdendencies. libXss.so.1 => not found
libatk-1.0.so.0 => not found
libgtk-3.so.0 => not found
libgdk-3.so.0 => not found
libgdk_pixbuf-2.0.so.0 => not found
libdbus-glib-1.so.2 => not found
Notice anything different?
That’s right, no more missing
libgconf-2.so.4
dependency for us.
We evidently traded it for a brand new libdbus-glib-1.so.2
dependency instead.
It essentially becomes a game of whack-a-mole at this point.
You fix one dependency and a new secondary dependency pops up.
As you might have guessed, it’s not much fun to do this by hand.
Definitely more of a script type of job.
Hmmm… somebody should really make one of those.
The basic gist of what the installation script does is to run
ldd
,
loop through the missing dependencies, find the CentOS 7 packages that
provide them, download and extract them, copy over the library files,
and then repeat until everything is right in the world.
It’s probably easiest to just see the code.# Loop through and install missing dependencies.
while true
do
finished=true
# Loop through each of the missing libraries for this round.
while read -r line
do
if [[ $line == *"/"* ]]; then
# Extract the filename when a path is present (e.g. /lib64/).
file=`echo $line | sed 's>.*/\([^/:]*\):.*>\1>'`
else
# Extract the filename for missing libraries without a path.
file=`echo $line | awk '{print $1;}'`
fi
# We'll require an empty round before completing.
finished=false
echo "Finding dependency for ${file}"
# Find the URL for the Centos 7 RPM containing this library.
url=$(repoquery --repofrompath=centos,http://mirror.centos.org/centos/7/os/`arch` \
--repoid=centos -q --qf="%{location}" --whatprovides $file | \
sed s/x86_64.rpm$/`arch`.rpm/ | \
sed s/i686.rpm$/`arch`.rpm/g
)
# Download the RPM.
wget "${url}" -O ${file}.rpm
# Extract it and remove it.
rpm2cpio ${file}.rpm | cpio -idmv
rm ${file}.rpm
# Copy it over to our library directory and clean up.
find . | grep /${file} | xargs -n1 -I{} sudo cp {} /opt/google/chrome/lib/
rm -rf *
done < <(ldd /opt/google/chrome/chrome 2>&1 | grep -e "no version information" -e "not found")
# Break once no new files have been copied in a loop.
if [ "$finished" = true ]; then
break
fi
done
If you copy and paste that big hunk of code into your terminal, it
should download and install all of the dependencies that you need.
Once the dust settles, there will be about 30 library files that have
been added to
/opt/google/chrome/lib/
.
If my calculations are correct… everything should just work at this point.
To verify this, we can run Google Chrome in headless mode and take a screenshot
google-chrome-stable --headless --disable-gpu --screenshot \
https://intoli.com/blog/installing-google-chrome-on-centos/
which will produce a beautiful
screenshot.png
file.
------------------------------------------------------------------
https://intoli.com/blog/installing-google-chrome-on-centos/
Comments
Post a Comment